Правильно ли сравнивать два округленных числа с плавающей запятой с помощью оператора ==?

Или есть шанс, что операция не удастся?

Спасибо.

Я выбрал неправильный термин, и на самом деле я имел в виду округление до 0, а не усечение.

Дело в том, что мне нужно сравнить целочисленную часть двух двойников, и я просто привожу их к int, а затем использую ==, но, как кто-то указал в одном из моих предыдущих вопросов, это может вызвать исключение переполнения, если double не может вписаться в целое число.

Таким образом, возникает вопрос: «Правильно ли использовать оператор == для сравнения двух двойных значений, которые ранее были округлены до 0, или мне следует придерживаться метода приведения к типу int и поймать возможное исключение?


person Trap    schedule 25.10.2008    source источник
comment
stackoverflow.com/ вопросы/17333/   -  person OrangeDog    schedule 16.05.2012


Ответы (6)



Еще хуже то, что иногда, даже для одного и того же числа, это не удается. Это связано с тем, что некоторые компиляторы или процессоры будут использовать больше битов точности в регистре ЦП, чем в памяти (например, у MSVC есть 3 различных варианта поведения с плавающей запятой). Таким образом, недавно вычисленное значение может не иметь усеченных битов и будет выглядеть неравным. НИКОГДА не используйте == для поплавков.

person rlbond    schedule 13.03.2009

Он все еще может выйти из строя из-за обычных проблем с представлением с плавающей запятой. Вместо их усечения используйте дельту, которая будет представлять эквивалентную точность.

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

10.19999999

10.20000001

но когда вы усекаете их, они дают разные результаты.

10.19

10.20

Принимая во внимание, что если бы я использовал дельту 0,001 для сравнения разницы, я бы увидел, что эти два значения фактически одинаковы.

person Bill the Lizard    schedule 25.10.2008

Никогда нельзя использовать == с плавающей запятой.

Что означает «усечение» в контексте с плавающей запятой? Какую конкретную библиотечную функцию вы вызываете? Какой результат? Что заставляет вас полагать, что «усеченные» значения более сопоставимы, чем неусеченные значения?

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

Однако если вы конвертируете в целое число, вы можете использовать ==.

person S.Lott    schedule 25.10.2008
comment
Никогда не говори никогда. Если у вас есть относительное вычисление, в котором используются числа, которые точно представляются в IEEE 754, то можно использовать ==, но такие ситуации встречаются редко. - person Adam Rosenfield; 25.10.2008
comment
@Adam Rosenfield: точно представляемое предложение не имеет значения. Если вы знаете результат выражения, вы просто используете ответ. Если вы написали программу, вы не знали результата и не знали, можно ли его точно представить. Никогда не используйте ==. - person S.Lott; 25.10.2008
comment
Что, если вы пишете модульный тест для чего-то, что выполняет некоторую математику с плавающей запятой? Если вы точно знаете, каким должен быть ответ, вы должны использовать == в этом случае. Никогда не говори никогда. - person Adam Rosenfield; 26.10.2008
comment
@ Адам Розенфилд: Все еще не согласен. Это модульный тест, а не прикладное программирование. Модульные тесты могут быть хрупкими и зависеть от конкретных версий библиотек и оборудования. Это исключение настолько редкое, настолько незначительное и так легко ошибиться, что я остаюсь при своем сумасшедшем абсолютизме. Никогда не используйте ==. - person S.Lott; 26.10.2008
comment
Я использовал == 0 только сегодня. Это достаточно безопасно. Метод, вычисляющий площадь перекрытия между двумя прямоугольниками, возвращает 0, если перекрытия нет. Конечно, я могу проверить, равен ли результат нулю. - person Qwertie; 18.12.2010
comment
@Qwertie - но что, если результат вашего расчета равен -0 или 2e-1074, считается ли это отсутствием совпадения? - person OrangeDog; 29.03.2012
comment
@Qwertie: Достаточно безопасно? Что это может означать? Если вы используете значения с плавающей запятой, а ваши прямоугольники огромны, то перекрытие может быть ненулевым, но в конечном итоге будет усечено до нуля из-за проблем с вычислением чисел с плавающей запятой. Нельзя сравнивать с нулем. - person S.Lott; 31.03.2012
comment
@ S.Lott Достаточно безопасно зависит от приложения. Если вам действительно нужно обнаружить 0,00000000000000001% перекрытия между фантастически большими прямоугольниками, ну, я думаю, просто на всякий случай детектор перекрытий может вернуть -1, чтобы представить отсутствие перекрытия, чтобы отличить его от 0, что означает перекрытие настолько мало, что оно было округлено до 0, и тогда вызывающая сторона может вместо этого безопасно выполнить перекрытие (a, b) == -1 для результата. Но, конечно, для моего приложения и большинства других возврат 0 достаточно однозначен. - person Qwertie; 19.04.2012
comment
@OrangeDog, (-0 == 0) == правда. И моя функция не возвращает 2e-1074, когда обнаруживает, что перекрытия нет, она возвращает ровно 0. - person Qwertie; 19.04.2012
comment
@Qwertie, ты, кажется, упускаешь суть. Если есть перекрытие 0,00000000000000001% (которое, как мы согласны, вероятно, должно считаться отсутствием перекрытия), то выполнение getOverlap() == 0 будет ложным (т. Е. Это говорит о том, что есть перекрытие). - person OrangeDog; 19.04.2012
comment
@OrangeDog Что ж, если функция возвращает что-либо, отличное от нуля, будь то 2e-1074 или что-то еще, тогда (getOverlap () == 0) == false, что ХОРОШО, поскольку есть перекрытие. Я не говорил, что 0,00000000000000001% перекрытия не должны считаться любым перекрытием, я сказал, что для моего приложения сравнение с 0 достаточно однозначно, что означает, что в случае бесконечно малого перекрытия оно не учитывается. ни хрена не имеет значения, верно это сравнение или нет. - person Qwertie; 06.06.2012


Если ваше абсолютное значение меньше 2 ^ 23 для одинарного или 2 ^ 52 для двойного, вы можете использовать round(), а затем выполнить сравнение. Большие значения не могут быть точно сохранены, и это открывается для ситуаций, когда N == N+1.

person Martin Liesén    schedule 25.10.2008
comment
Это потребует округления, а не усечения. Конечно, я не совсем уверен, что ОП подразумевает под усечением (ceil()?), но, предположительно, если бы он имел в виду округление, он бы так и сказал... - person Shog9; 25.10.2008
comment
Ceil — это не то же самое, что Trunc, когда дело доходит до отрицательных чисел. Моя точка зрения заключалась в том, что если исключить округление/усечение/потолок/пол из уравнения, вы можете безопасно проводить сравнение. - person Martin Liesén; 25.10.2008