Java: сравнение двойных значений - PullRequest
22 голосов
/ 12 января 2009

Нужно ли быть осторожным при сравнении двойного значения с нулем?

if ( someAmount <= 0){
.....
}

Ответы [ 7 ]

18 голосов
/ 12 января 2009

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

double epsilon = 0.0000001;
if      ( f <= ( 0 - epsilon ) ) { .. }
else if ( f >= ( 0 + epsilon ) ) { .. }
else { /* f "equals" zero */ }

Или вы можете просто округлить двойники до определенной точности, прежде чем переходить на них.

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

9 голосов
/ 12 января 2009

Для равенства: (т.е. == или !=) да.

Для других сравнительных операторов (<, >, <=, >=) это зависит от того, находитесь ли вы на граничных случаях, например, является ли < эквивалентным <=, что является еще одним случаем равенства. Если вас не волнуют крайние случаи, это обычно не имеет значения, хотя это зависит от того, откуда берутся ваши входные числа и как они используются.

Если вы ожидаете, что (3.0/10.0) <= 0.3 будет оцениваться как true (это может не произойти, если ошибка с плавающей запятой приводит к тому, что значение 3.0 / 10.0 будет немного больше 0,3, как 0.300000000001), и ваша программа будет вести себя плохо, если она оценивает как false - это крайний случай, и вы должны быть осторожны.

Хорошие численные алгоритмы почти никогда не должны зависеть от равенства и краевых случаев. Если у меня есть алгоритм, который принимает в качестве входных данных 'x', любое число от 0 до 1, в общем случае не имеет значения, 0 < x < 1 или 0 <= x <= 1. Однако есть исключения: вы должны быть осторожны при оценке функций с точками ветвления или особенностями.

Если у меня есть промежуточное значение y, и я ожидаю y >= 0, и я оцениваю sqrt(y), то я должен быть уверен, что ошибки с плавающей точкой не приводят к тому, что y будет очень маленьким отрицательным числом и функция sqrt() для выдачи ошибки. (Предполагая, что это ситуация, когда комплексные числа не задействованы.) Если я не уверен насчет числовой ошибки, я, вероятно, вместо этого оценим sqrt(max(y,0)).

Для таких выражений, как 1/y или log(y), в практическом смысле не имеет значения, является ли y нулем (в этом случае вы получаете ошибку особенности) или y является числом, очень близким к нулю (в этом случае вы я получу очень большое число, величина которого очень чувствительна к значению y) - оба случая "плохие" с числовой точки зрения, и мне нужно пересмотреть то, что я пытаюсь сделать, и что поведение, которое я ищу, когда y значения находятся в окрестности нуля.

6 голосов
/ 12 января 2009

В зависимости от того, как вычисляется someAmount, вы можете ожидать странного поведения с плавающей запятой / doubles

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

Чтобы узнать подробнее об этом, вы можете прочитать эту небольшую статью

Вам следует рассмотреть возможность использования java.lang.Math.signum или java.math.BigDecimal, особенно для валютных и налоговых расчетов

5 голосов
/ 12 января 2009

Остерегайтесь автоматической распаковки:

Double someAmount = null;
if ( someAmount <= 0){

Boom, NullPointerException.

1 голос
/ 07 февраля 2018

Да, вы должны быть осторожны.

Предложение: Одним из хороших способов было бы использовать BigDecimal для проверки равенства / неравенства 0:

BigDecimal balance = pojo.getBalance();//get your BigDecimal obj

0 != balance.compareTo(BigDecimal.ZERO)

Объяснение:

Функция compareTo() сравнивает это BigDecimal с указанным BigDecimal. Два BigDecimal объекта, которые равны по стоимости, но имеют различный масштаб (например, 2,0 и 2,00), считаются равными по этому методу. Этот метод предоставляется предпочтение отдельным методам для каждого из шести boolean операторов сравнения (<, ==, >, >=, !=, <=). Рекомендуемая идиома для выполнения этих сравнений: (x.compareTo(y) <op> 0), где один из шести операторов сравнения.

(благодаря документации SonarQube)

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

Даже простые задания с плавающей запятой не просты:

float f = 0.1; // 0.100000001490116119384765625
double d = 0.1; // 0.1000000000000000055511151231257827021181583404541015625

(результаты будут различаться в зависимости от параметров компилятора и компилятора);

Следовательно, использование операторов равенства (==) и неравенства (! =) Для значений с плавающей или двойной точностью почти всегда является ошибкой. Вместо этого лучше всего избегать сравнений с плавающей запятой. Когда это невозможно, вам следует рассмотреть возможность использования одного из Java-чисел, обрабатывающих float, таких как BigDecimal, который может правильно обрабатывать сравнения с плавающей запятой. Третий вариант - искать не равенство, а достаточно ли близкое значение. То есть сравните абсолютное значение разницы между сохраненным значением и ожидаемым значением с допустимой погрешностью. Обратите внимание, что это не охватывает все случаи (например, NaN и Infinity).

0 голосов
/ 03 мая 2016

Проверьте, что значение типа double или float равно 0, пороговое значение ошибки используется для определения, находится ли значение около 0, но не совсем 0. Я думаю, что этот метод - лучший из тех, что я встречал.

Как проверить, равен ли дубль нулю? Ответ @William Morrison

public boolean isZero(double value, double threshold){
  return value >= -threshold && value <= threshold;
}

Например, установить порог равным 0. Например,

System.out.println(isZero(0.00, 0));
System.out.println(isZero(0, 0));
System.out.println(isZero(0.00001, 0));

Результаты как истинные, истинные и ложные из приведенных выше примеров кодов.

Веселись @. @

0 голосов
/ 01 ноября 2009

Если вас не интересуют крайние случаи, просто попробуйте someAmount <= 0. Это делает смысл кода понятным. Если вам все равно, ну ... это зависит от того, как вы рассчитываете someAmount и почему вы проверяете неравенство.

...