Правильно ли сравнение двоичных чисел с плавающей точкой?
Я работаю над различными функциями манипуляции с блоком памяти, и во время тестов я заметил, что моя реализация IsEqualRange(double* begin1, double* end1, double* begin2, double* end2)
намного быстрее, чем std::equals(...)
на MSVC и GCC, а также. Дальнейшее исследование показало, что double и float не блокируются по сравнению с memcmp
, а в цикле for один за другим.
В какой ситуации бинарное сравнение чисел приводит к неверному результату? Когда это нормально для двоичного сравнения (равенства) массив чисел с плавающей запятой/удваивается? Есть ли другие фундаментальные типы, где я не должен использовать memcmp
?
Ответы
Ответ 1
Первое, что я хотел бы сделать на вашем месте, это проверить настройки оптимизации.
Хорошо использовать memcmp
для массива с плавающей точкой, но учтите, что вы можете получить результаты, отличные от element-by-element ==
. В частности, для IEEE754 с плавающей точкой:
-
+0.0 определяется для сравнения равным -0.0.
-
NaN определяется для сравнения не равно NaN.
Ответ 2
Основная проблема - это nan
значения, так как они никогда не равны себе. Есть также два представления 0 (+0
и -0
), которые равны, но не двоично равны.
Строго говоря, вы не можете использовать memcmp
для них, поскольку ответ будет математически неверным.
Если вы знаете, что у вас нет значений nan
или 0
, тогда вы можете использовать memcmp
.
Ответ 3
Двоичное сравнение работает со слишком большой точностью для многих реальных применений. Например, в javascript, 0.2/10.0! = 0.1 * 0.2 Если у вас есть две переменные, которые в итоге приводят к очень и очень небольшим ошибкам округления, они будут неравными, несмотря на то, что они представляют "одно и то же" число.