1

Мне нужно было написать алгоритм, который машинно независимо переводит байтовый массив, где байты лежат в формате 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; }

Denver Toha
  • 2,595

1 Answers1

7

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...).

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

HolyBlackCat
  • 27,445
  • 3
  • 27
  • 40
  • pow вообще использует либо табулированную встроенную функцию , либо комбинацию экспооненты и логарифма exp(power * log(base)) , точности ждать нельзя. – Swift - Friday Pie Oct 19 '21 at 22:47