Модуль с двойными в Java - PullRequest
       47

Модуль с двойными в Java

12 голосов
/ 12 июля 2010

Как вы справляетесь со странным поведением Java с оператором модуля при использовании double?

Например, вы ожидаете, что результат 3.9 - (3.9 % 0.1) будет 3.9 (и действительно, Google говорит, что я не схожу с ума), но когда я запускаю его на Java, я получаю 3.8000000000000003.

Я понимаю, что это результат того, как Java хранит и обрабатывает удваивается, но есть ли способобойти это?

Ответы [ 4 ]

18 голосов
/ 12 июля 2010

Используйте точный тип, если вам нужен точный результат:

    double val = 3.9 - (3.9 % 0.1);
    System.out.println(val); // 3.8000000000000003

    BigDecimal x = new BigDecimal( "3.9" );
    BigDecimal bdVal = x.subtract( x.remainder( new BigDecimal( "0.1" ) ) );
    System.out.println(bdVal); // 3.9

Почему 3.8000 ... 003? Потому что Java использует FPU для вычисления результата. 3.9 невозможно хранить точно в нотации двойной точности IEEE, поэтому он хранит 3.89999 ... И 3,8999% 0,01 дает 0,09999 ... следовательно, результат немного больше, чем 3,8.

4 голосов
/ 12 июля 2010

С Спецификация языка Java :

Результат с плавающей точкой операция остатка, вычисленная % оператор не такой как производится оставшейся операцией определяется IEEE 754. IEEE 754 Остальная операция вычисляет остаток от округления, не усеченное деление, и поэтому его поведение не аналогично обычный целочисленный оператор остатка. Вместо этого язык программирования Java определяет% для операций с плавающей точкой вести себя аналогично что из целочисленного остатка оператор; это можно сравнить с функция библиотеки C fmod. IEEE 754 остаток операции может быть вычисляется библиотечной программой Math.IEEEremainder.

Другими словами, это связано с тем, что Java округляет результат деления, связанного с вычислением остатка, в то время как IEEE754 определяет усечение ответа деления. Этот конкретный случай, по-видимому, очень четко раскрывает эту разницу.

Вы можете получить ожидаемый ответ, используя Math.IEEEremainder:

System.out.println(3.9 - (3.9 % 0.1));
System.out.println(3.9 - Math.IEEEremainder(3.9, 0.1));
2 голосов
/ 12 июля 2010

Вы можете использовать java.math.BigDecimal и его метод divAndRemainder ().

1 голос
/ 12 июля 2010

Если вы знаете количество десятичных знаков, с которыми вы имеете дело, вы можете сначала попытаться преобразовать их в целые числа. Это всего лишь классический случай неточности с плавающей запятой. Вместо 3.9 % 0.1 вы делаете что-то вроде 3.899 % 0.0999

...