Правильно ли сравнение двоичных чисел с плавающей точкой?

Я работаю над различными функциями манипуляции с блоком памяти, и во время тестов я заметил, что моя реализация IsEqualRange(double* begin1, double* end1, double* begin2, double* end2) намного быстрее, чем std::equals(...) на MSVC и GCC, а также. Дальнейшее исследование показало, что double и float не блокируются по сравнению с memcmp, а в цикле for один за другим.

В какой ситуации бинарное сравнение чисел приводит к неверному результату? Когда это нормально для двоичного сравнения (равенства) массив чисел с плавающей запятой/удваивается? Есть ли другие фундаментальные типы, где я не должен использовать memcmp?

Ответ 1

Первое, что я хотел бы сделать на вашем месте, это проверить настройки оптимизации.

Хорошо использовать memcmp для массива с плавающей точкой, но учтите, что вы можете получить результаты, отличные от element-by-element ==. В частности, для IEEE754 с плавающей точкой:

  1. +0.0 определяется для сравнения равным -0.0.

  2. NaN определяется для сравнения не равно NaN.

Ответ 2

Основная проблема - это nan значения, так как они никогда не равны себе. Есть также два представления 0 (+0 и -0), которые равны, но не двоично равны.

Строго говоря, вы не можете использовать memcmp для них, поскольку ответ будет математически неверным.

Если вы знаете, что у вас нет значений nan или 0, тогда вы можете использовать memcmp.

Ответ 3

Двоичное сравнение работает со слишком большой точностью для многих реальных применений. Например, в javascript, 0.2/10.0! = 0.1 * 0.2 Если у вас есть две переменные, которые в итоге приводят к очень и очень небольшим ошибкам округления, они будут неравными, несмотря на то, что они представляют "одно и то же" число.