Случаи, когда числа с плавающей точкой сравнимы с использованием равенства
Недавно я вступил в дискуссию о сравнении с плавающей запятой. Моя точка зрения всегда заключалась в том, чтобы не сравнивать два числа с плавающей запятой, используя ==
непосредственно.
Было отмечено, что это не так, и есть случаи, когда использование ==
отлично в порядке. Я могу вспомнить типичные случаи, когда я проверяю по литералам IEEE 754, как +-INF
или же +-0
Но кроме этого, я не могу придумать случай, когда это не приведет к проблемам.
Итак, мой вопрос: в каких случаях сравнение с плавающей запятой с использованием ==
является действительным?
3 ответа
Представление с плавающей запятой двойной точности (64-разрядное на число) является точным для целых чисел до -+2**53 (-+ 9 007 199 254 740 992). Если вы используете числа с плавающей запятой, но начинаете с целых чисел и делаете целочисленные вычисления с ними, и вы никогда не превышали этот предел, тогда результат является точным и использует ==
отлично в порядке.
Числа, которые в общем случае могут быть представлены точно, представляют собой N/M, где N - целое число, а M - степень двойки. Таким образом, если вы просто выполняете вычисления, включающие, например, 1/4, 1/2, 3/4 и их целочисленные кратные, вы тоже будете в порядке, пока не достигнете очень больших множителей.
Когда вместо этого вы имеете дело с числами, которые не могут быть представлены точно (например, 0,1), приближение привело меня к неожиданным результатам. Одним из источников проблем является то, что промежуточные результаты могут храниться во временных данных с более высокой точностью, и, следовательно, результат формулы может отличаться в зависимости от того, явно ли вы храните его в памяти или нет, и он также может меняться в зависимости от уровня оптимизации.
Вот несколько примеров правильного использования равенства с плавающей точкой:
когда функция задокументирована как возвращающая
HUGE_VAL
в некоторых случаях определение того, произошло ли это сresult == HUGE_VAL
,определение, если двойной
d
содержит число, представимое в видеfloat
:d == (double)(float)d
, Я на самом деле использую это в своей повседневной работе, потому что я использую пары двойников для представления как интервалов двойных значений, так и интервалов значений с плавающей запятой, и есть моменты, в которых приятно иметь возможность утверждать, что границы интервала с плавающей запятой поплавки.определение, является ли число с плавающей точкой
y
являетсяNaN
:y == y
,определение, является ли число с плавающей точкой
y
это бесконечность или NaN сy - y == 0.0
(конечные значенияy
выполнить условие,NaN
и бесконечности делают это ложным).определение, установлен ли бит в значении и числа с плавающей запятой, как в примере ниже, взятом из этой выкладки.
/* coef is a power of two plus one. */ double t = coef * f; double o = f - t + t - f; if (o != 0) { ...
Числа с плавающей запятой представляют собой точные значения с использованием соответствующей базы (обычно 2). Нет ничего плохого в том, чтобы сравнивать их, используя равенство.
Двоичные числа с плавающей запятой не могут точно представлять все десятичные значения, т. Е. Для большинства дробных десятичных значений двоичная с плавающей запятой будет использовать приближение. Пока десятичные числа не превышают std::numeric_limits<F>::digits10
результирующее представление также однозначно идентифицируется внутри системы (для некоторых десятичных значений существует выбор между двумя двоичными представлениями, и в этом случае направление округления должно выбирать правильное).
Проблема, которая делает числа с плавающей точкой странным, состоит в том, что вычисления приводят к округлению значений и в зависимости от того, когда происходит округление, предположительно точные операции являются неточными, и порядок оценки имеет значение. Выполнение арифметики с округленными значениями соответственно увеличит ошибки и даст другие значения, отличные от полученных, например, путем преобразования десятичного значения в двоичную с плавающей запятой. Вы, вероятно, не хотите использовать равные операции для результатов вычислений.