Почему в 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 шт):
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...).
Переделайте на операторах битового сдвига: <<, >>.