Почему в 2 логически одинаковых алгоритмах получаются разные ответы

Мне нужно было написать алгоритм, который машинно независимо переводит байтовый массив, где байты лежат в формате little-endian в число. Когда я тестил его я заметил, что на 8-байтовых числах ответ не совпадает. Методом тыка нашел как исправить этот баг, но я не понимаю почему это работает. Для удобства на каждом шаге я выводил как байты лежат в числе, и заметил что на 2 последних шагах 2 младших байта самопроизвольно изменяются во втором варианте. 2 этих варианта логически не отличаются друг от друга, почему так происходит?

#include <iostream>

#include <vector>
#include <math.h>


int main() {
  std::vector<uint8_t> test{0xE3, 0x17, 0xFF, 0x70, 0xE6, 0x18, 0xF1, 0x73};
  //правильно
  uint64_t res = 0;
  for (size_t i = 0; i < 8; i++) {
    uint64_t  buf = test[i] * pow(256, i);
    res += buf;
    std::cout << std::hex << res << std::endl;
  }
  std::cout << std::dec << res;

  std::cout << std::endl;
  //неправильно
  res = 0;
  for (size_t i = 0; i < 8; i++) {
    res += test[i] * pow(256, i);
    std::cout << std::hex << res << std::endl;
  }

  std::cout << std::dec << res;
}

Ответы (1 шт):

Автор решения: HolyBlackCat

pow оперирует числами с плавающей точкой, в данном случае - double. Он преобразует к double аргументы и возвращает тоже double.

Когда вы умножаете на него test[i], он преобразуется в double. Это происходит одинаково в обоих примерах.

Отличие в изменении res. В первом случае результат умножения сначала преобразуется в uint64_t (с отбрасыванием дробной части), потом суммируется с res.

Во втором примере res сам преобразуется в double, а потом результат суммирования обратно преобразуется в uint64_t.

Длинное 64-битное число не влезает в double, у которого только 52 бита мантиссы, поэтому младшие биты зануляются.

Зато результат умножения test[i] * pow(256, i) влезает без проблем, потому что требует не больше 7 бит мантиссы...


Не используйте pow для целочисленных вычислений. Даже без этой проблемы, он может выдавать неточный ответ (например, вот тут получили pow(5, 2) == 24.999...).

Переделайте на операторах битового сдвига: <<, >>.

→ Ссылка