Можете ли вы сравнить значения с плавающей запятой точно до нуля? - PullRequest
24 голосов
/ 04 марта 2010

Я знаю, что мы не можем сравнивать 2 значения с плавающей запятой, используя ==. Мы можем только сравнить, что они находятся в некотором интервале друг от друга. Я знаю

if(val == 0.512)

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

if (val in (0.512-epsilon, 0.512+epsilon))

Но 0 особенный? Можем ли мы сравнить поплавки ровно с 0? Или даже это неправильно? Особенно в контексте C # и Java?

double val = 0;
val = getVal();
if(val == 0)

Ответы [ 8 ]

17 голосов
/ 04 марта 2010

Даже если 0 имеет точное представление, вы не можете полагаться на то, что результат вычисления с использованием чисел с плавающей точкой будет равен 0. Как вы заметили, это связано с вычислением с плавающей запятой и проблемами преобразования.

Итак, вы должны проверить на 0 против вашей толерантности к эпсилону.

13 голосов
/ 04 марта 2010

Используйте допуск / ephsilon.

Я только что оценил следующее в Java, которое математически дает ноль:

1.0/5.0 + 1.0/5.0 - 1.0/10.0 - 1.0/10.0 - 1.0/10.0 - 1.0/10.0

и фактически получил

2.7755575615628914E-17
13 голосов
/ 04 марта 2010

Вы можете сравнить с нулем, если вы присвоили этой переменной ноль. Если вы получаете ноль от, например. вычитание вы можете получить очень маленькое число, близкое к нулю. Например: 0,1-0,1 может перевести в нечто вроде 1e-9.

4 голосов
/ 07 марта 2010

Для сравнения с ошибкой все, что вам нужно, это.

// compare a and b with an ERR error.
if (Math.abs(a - b) <= ERR)

Для сравнения с 0

// compare a and 0 with an ERR error.
if (Math.abs(a) <= ERR)
3 голосов
/ 04 марта 2010

Поскольку ноль имеет точное представление, можно сравнить значение == с нулем. Если переменная, которую вы тестируете, была задана с помощью присваивания или из значения, введенного в (как, например, getVal в вашем примере?), Оно может легко быть нулевым Но если это был результат расчета, шансы, что он точно равен нулю, очень малы. Это усугубляется тем, что обычные десятичные дроби, такие как 0,2, не имеют точного представления в плавающей запятой. Вот почему лучше использовать эпсилон.

2 голосов
/ 04 марта 2010

Я бы по-прежнему рекомендовал следовать толерантности и не сравнивать с нулем точно

0 голосов
/ 06 октября 2010

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

Не повторяя сказанного другими, я просто хочу подчеркнуть тот факт, что использование подхода толерантности является более будущим . То, что вы когда-то считаете простым заданием, может позже привести к реальной арифметике. Использование обнаженного сравнения делает больно непонятным отладку на более позднем этапе.

0 голосов
/ 04 марта 2010

Я не думаю, что вы можете вообще - вычисление, которое происходит в getVal (), может логически привести к нулю, но это не означает, что он вернет ноль. Если вы явно возвращаете ноль, чтобы указать какое-то условие, тогда сравнение всегда должно работать, но я не думаю, что это будет наилучшей практикой. Я бы изменил функцию, чтобы она возвращала код состояния и передавала значение, которое нужно изменить по ref.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...