По модулю не работает - PullRequest
       2

По модулю не работает

6 голосов
/ 27 сентября 2011

Я знаю, это будет казаться очень глупым вопросом, но я просто не понимаю, почему это не работает.Это:

 System.out.println(5 % .10);

И оно возвращается:

0.09999999999999973

У меня действительно НЕТ ИДЕИ.Я только изучаю Java, и я довольно хорошо разбираюсь с C #, поэтому я попробовал с C # to.C #, похоже, тоже возвращает то же самое.

Ответы [ 5 ]

8 голосов
/ 27 сентября 2011

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

Вы должны использовать BigDecimal , в этом случае метод остатка для точной арифметикис десятичными знаками.

BigDecimal number = new BigDecimal(5);
BigDecimal divisor = new BigDecimal("0.1");
BigDecimal result = number.remainder(divisor);
System.out.println(result); // 0
8 голосов
/ 27 сентября 2011

Это связано с точностью с плавающей точкой. 0.10 нельзя представить точно в двоичном виде. Следовательно, результат не совсем 0.10 и, следовательно, не изменяется до 0.

Это будет работать с 0.25, потому что 0.25 может быть представлен точно в двоичном виде.

EDIT:

Как правило, любое число, которое может быть выражено в виде дроби со степенью двойки в знаменателе, может быть выражено точно в IEEE с плавающей точкой. (при условии, что оно не будет чрезмерным / недостаточным)

2 голосов
/ 27 сентября 2011

Вы делаете арифметику с плавающей запятой.0.1 не имеет точного представления в виде числа с плавающей точкой.

1 голос
/ 27 сентября 2011

Двоичное представление для 0.1:

System.out.println(new BigDecimal(0.1));

печать

0.1000000000000000055511151231257827021181583404541015625

Когда вы печатаете 0.1, вы получаете небольшое количество округлений, которое скрывает эту ошибку.

При выполнении вычисления необходимо использовать BigDecimal или округлить результат или преобразовать вычисление, чтобы минимизировать ошибку.

5 % 0.1
(5 / 0.1 % 1) * 0.1
50 % 1 / 10

С точки зрения двойного вы можете сделать

double d = 5;
double mod0_1 = d * 10 % 1 / 10;
double rounded = Math.round(mod0_1 * 1e12)/1e12; // round to 12 places.

Примечание: в результате все равно может быть небольшая ошибка, но она будет достаточно маленькой, чтобы при печати ее не было видно.

1 голос
/ 27 сентября 2011

Java (как и большинство языков программирования, за исключением COBOL) выполняет вычисления в двоичных системах. Я думаю, что 0.10 имеет такое неудачное двоичное представление, что результат ваших вычислений выглядит следующим образом. Я думаю, что лучше всего избегать вычисления модуля double или float и придерживаться целого или длинного, чтобы получить лучшие результаты.

...