В следующей программе вы можете видеть, что каждое значение немного меньше, чем .5
округлено в меньшую сторону, кроме 0.5
.
for (int i = 10; i >= 0; i--) {
long l = Double.doubleToLongBits(i + 0.5);
double x;
do {
x = Double.longBitsToDouble(l);
System.out.println(x + " rounded is " + Math.round(x));
l--;
} while (Math.round(x) > i);
}
печать
10.5 rounded is 11
10.499999999999998 rounded is 10
9.5 rounded is 10
9.499999999999998 rounded is 9
8.5 rounded is 9
8.499999999999998 rounded is 8
7.5 rounded is 8
7.499999999999999 rounded is 7
6.5 rounded is 7
6.499999999999999 rounded is 6
5.5 rounded is 6
5.499999999999999 rounded is 5
4.5 rounded is 5
4.499999999999999 rounded is 4
3.5 rounded is 4
3.4999999999999996 rounded is 3
2.5 rounded is 3
2.4999999999999996 rounded is 2
1.5 rounded is 2
1.4999999999999998 rounded is 1
0.5 rounded is 1
0.49999999999999994 rounded is 1
0.4999999999999999 rounded is 0
Я использую Java 6 обновление 31.
java
floating-point
double
rounding
Питер Лори
источник
источник
0.5
к числу и затем используяfloor
; Java 7 больше не документирует это таким образом (вероятно / надеюсь, потому что они исправили это).Ответы:
Резюме
В Java 6 (и предположительно ранее)
round(x)
реализован так жеfloor(x+0.5)
. 1 Это ошибка спецификации, именно для этого одного патологического случая. 2 Java 7 больше не требует этой неработающей реализации. 3Проблема
0.5 + 0.49999999999999994 - ровно 1 с двойной точностью:
Это связано с тем, что показатель 0,49999999999999994 имеет меньший показатель степени, чем 0,5, поэтому при их добавлении его мантисса смещается, а ULP увеличивается.
Решение
Начиная с Java 7, OpenJDK (например) реализует это так: 4
1. http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#round%28double%29
2. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675 (кредит @SimonNickerson за это)
3. http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#round%28double%29
4. http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Math.java#Math.round%28double%29
источник
round
в Javadoc дляMath.round
или в обзореMath
класса.Похоже, это известная ошибка (ошибка Java 6430675: Math.round имеет удивительное поведение для 0x1.fffffffffffffp-2 ), которая была исправлена в Java 7.
источник
Исходный код в JDK 6:
Исходный код в JDK 7:
Когда значение равно 0,49999999999999994d, в JDK 6 он вызовет floor и, следовательно, вернет 1, но в JDK 7
if
условие проверяет, является ли число наибольшим двойным значением меньше 0,5 или нет. Так как в этом случае число не является наибольшим двойным значением меньше 0,5, поэтомуelse
блок возвращает 0.Вы можете попробовать 0.49999999999999999d, который вернет 1, но не 0, потому что это наибольшее двойное значение меньше 0,5.
источник
floor
метод округляет его правильно.У меня то же самое на 32-битной JDK 1.6, но на 64-битной Java 7 у меня 0 для 0.49999999999999994, округленное 0, и последняя строка не печатается. Кажется, это проблема виртуальной машины, однако при использовании чисел с плавающей запятой следует ожидать, что результаты будут немного отличаться в разных средах (процессор, 32- или 64-разрядный режим).
И при использовании
round
или инвертировании матриц и т. Д. Эти биты могут иметь огромное значение.выход x64:
источник
Ниже приводится выдержка из сообщения об ошибке Oracle 6430675 по адресу. Посетите отчет для полного объяснения.
Методы {Math, StrictMath.round операционно определены как
для двойных аргументов. Хотя это определение обычно работает как положено, оно дает неожиданный результат 1, а не 0, для 0x1.fffffffffffffp-2 (0.49999999999999994).
Значение 0,49999999999999994 - это наибольшее значение с плавающей запятой меньше 0,5. В качестве шестнадцатеричного литерала с плавающей точкой его значение равно 0x1.fffffffffffffp-2, что равно (2 - 2 ^ 52) * 2 ^ -2. == (0,5 - 2 ^ 54). Следовательно, точное значение суммы
1 - 2 ^ 54. Это на полпути между двумя соседними числами с плавающей запятой (1 - 2 ^ 53) и 1. В арифметическом округлении IEEE 754 до ближайшего четного режима округления, используемого Java, когда результаты с плавающей запятой неточны, чем ближе эти два представимые значения с плавающей точкой, которые заключают в скобки точный результат, должны быть возвращены; если оба значения в равной степени близки, возвращается то, которое его последний бит ноль. В этом случае правильное возвращаемое значение из сложения равно 1, а не наибольшему значению меньше 1.
Пока метод работает так, как определено, поведение на этом входе очень удивительно; спецификация может быть изменена на что-то более похожее на «Округление до ближайшего длинного, округление связей», что позволит изменить поведение на этом входе.
источник