Первое я вообще не понимаю. На то, что два вычисленных идентичным образом числа могут быть не равны побитно, я когда-то давно сам налетал, но причину так окончательно и не понял (погрешность "в последнем бите" при вычислении допускает сам процессор? Но как, это же железка с жёстким алгоритмом?). Но тогда разность получается вполне разумной, порядка младшего бита мантиссы, здесь же и мантисса нулевая(?!?), и порядок ни с чем не бьётся, и "ноль (мантисса) умножить на не-ноль (порядок)" внезапно даёт не ноль...
Второй пример таки да, очевиден: конечные десятичные дроби вовсе необязательно конечны и в двоичном виде...
.equals() может иметь допуск на уровне последнего бита или двух мантиссы, println() тоже может "слегка округлять", так что это как раз неудивительно - бинарные представления не равны, но мы это не видим, потому что нам показывают округлённо-приблизительно. А вот какой механизм того, что они не равны, и их разность не равна нулю, более того, равна такому нулю (0E-51), который не равен нулю (0Е0) - я не понимаю.
Не. В этом я разобрался. В бигдецимале мантисса, порядок, точность и ещё разных внутренних значений. Если они различаются, equals() возвращает false. При этом числа могут быть равными. Что мы имеем в случае {0, -51} против {0, 0}. Как ява объекты они не равны, а как числа равны.
Поэтому
System.out.println(BigDecimal.ZERO.equals(three)); // false объекты не равны System.out.println(BigDecimal.ZERO.compareTo(three)); // 0 числа равны
Я так давно не занимался простыми вещами, что начал забывать, что числа с плавающей точкой не надо сравнивать методом equals().
А "ненулевой ноль" как-то объясняется? Или работа с плавающими числами в процессоре действительно настолько мистична, что процессор может родить ноль, который сам потом не будет считать нулём?
Не, это особенности метода equals. Он сравнивает, равен ли один объект другому объекту. А не равно ли одно число другому числу. То есть, 0e-51 и 0e0 численно равны. Но не равны как объекты, потому что представлены по-разному. С точки зрения объектно-ориентированной машины 0, 0.0 и 0.00000 это разные вещи. Хотя, школьная математика считает, что это одно и то же :) Поэтому если я хочу сравнить два числа, правильно использовать compareTo(), а если два объекта, то equals(), а если две ссылки на объекты, то ==.
Заглянул в википедию, узнал что там ноль ещё и имеет знак (мантисса хранится не как целое-со-знаком, а как целое-без-знака и отдельно знак), что отдельно добавляет хаоса.
Ну, и я когда-то с обычными double налетал на то, что две переменных, вычисленных по одной и той же формуле из одних и тех же исходных чисел, имели ненулевую разницу порядка младшего бита мантиссы. Это было несколько неожиданно :-)
Любопытно, а 0.0 и 0.000 реально не будут equals(), бинарное представление самого числа ведь должно совпадать с ZERO? Что ж там тогда отличаться будет?
Там есть отдельное поле "точность". Если оно не совпадает, закономерным образом объекты не равны. При том, что представляют они одно и то же число ноль.
А вот тут я что-то не соображу, что происходит. Два биг децимала равны. Но их разность не ноль. Более того, разность числа с самим собой не ноль, а 0E-51. Точнее, 0E-51 это ноль. Но не зеро.
Так вы ж только что писали, что "Я в курсе и причин и следствий", а "тут" первый пример из поста, просто развёрнутый на несколько строчек :-) Следствия более-менее ясны, а вот причины я как-то пока не понимаю.
> То есть, они не равны как объекты, но численно равны.
compareTo() опять-таки может иметь допуск в последних одном-двух битах мантиссы, как раз чтобы его можно было относительно безопасно использовать в вычислениях, так что compareTo()==0 ещё не означает что они вот прямо совсем-совсем равны, они могут отличаться. А вот механизм этого, повторюсь, я не понимаю.
compareTo() для бигдецималов сравнивает именно числа. Больше, меньше, или равно. Поэтому для него 0.0 и 0.0000 равны. При том что это не идентичные объекты.
Что-то смахивающее на "ненормализованный ноль". Тоже упомянутый у Кнута. И смысл - что вплоть до 10-50 разность нулевая, а за ещё меньшие разряды не отвечаем...
Истязайте себя Кнутом. Дональдом. Применительно к данному вопросу - т.2, гл. 4. Ну, или пособиями по численным методам.
Reply
И большинство в курсе, поэтому многие начинают забывать.
Но жизнь иногда напоминает.
В данном случае вопрос был вообще в применимости BigDecimal.ZERO.equals(valueFromModel)
Reply
Первое я вообще не понимаю. На то, что два вычисленных идентичным образом числа могут быть не равны побитно, я когда-то давно сам налетал, но причину так окончательно и не понял (погрешность "в последнем бите" при вычислении допускает сам процессор? Но как, это же железка с жёстким алгоритмом?). Но тогда разность получается вполне разумной, порядка младшего бита мантиссы, здесь же и мантисса нулевая(?!?), и порядок ни с чем не бьётся, и "ноль (мантисса) умножить на не-ноль (порядок)" внезапно даёт не ноль...
Второй пример таки да, очевиден: конечные десятичные дроби вовсе необязательно конечны и в двоичном виде...
Reply
BigDecimal one = new BigDecimal(1.001);
BigDecimal two = new BigDecimal(1.001);
System.out.println(one); // 1.000999999999999889865875957184471189975738525390625
System.out.println(two); // 1.000999999999999889865875957184471189975738525390625
System.out.println(one.equals(two)); // true
BigDecimal three = one.subtract(two);
System.out.println(three); // 0E-51
System.out.println(BigDecimal.ZERO.equals(three)); // false
Числа one и two равны, но их разность не равна нулю.
Reply
.equals() может иметь допуск на уровне последнего бита или двух мантиссы, println() тоже может "слегка округлять", так что это как раз неудивительно - бинарные представления не равны, но мы это не видим, потому что нам показывают округлённо-приблизительно. А вот какой механизм того, что они не равны, и их разность не равна нулю, более того, равна такому нулю (0E-51), который не равен нулю (0Е0) - я не понимаю.
Reply
В этом я разобрался.
В бигдецимале мантисса, порядок, точность и ещё разных внутренних значений.
Если они различаются, equals() возвращает false. При этом числа могут быть равными.
Что мы имеем в случае {0, -51} против {0, 0}. Как ява объекты они не равны, а как числа равны.
Поэтому
System.out.println(BigDecimal.ZERO.equals(three)); // false объекты не равны
System.out.println(BigDecimal.ZERO.compareTo(three)); // 0 числа равны
Я так давно не занимался простыми вещами, что начал забывать, что числа с плавающей точкой не надо сравнивать методом equals().
Reply
А "ненулевой ноль" как-то объясняется? Или работа с плавающими числами в процессоре действительно настолько мистична, что процессор может родить ноль, который сам потом не будет считать нулём?
Reply
Он сравнивает, равен ли один объект другому объекту. А не равно ли одно число другому числу.
То есть, 0e-51 и 0e0 численно равны. Но не равны как объекты, потому что представлены по-разному.
С точки зрения объектно-ориентированной машины 0, 0.0 и 0.00000 это разные вещи. Хотя, школьная математика считает, что это одно и то же :)
Поэтому если я хочу сравнить два числа, правильно использовать compareTo(), а если два объекта, то equals(), а если две ссылки на объекты, то ==.
Reply
Заглянул в википедию, узнал что там ноль ещё и имеет знак (мантисса хранится не как целое-со-знаком, а как целое-без-знака и отдельно знак), что отдельно добавляет хаоса.
Ну, и я когда-то с обычными double налетал на то, что две переменных, вычисленных по одной и той же формуле из одних и тех же исходных чисел, имели ненулевую разницу порядка младшего бита мантиссы. Это было несколько неожиданно :-)
Reply
Reply
Любопытно, а 0.0 и 0.000 реально не будут equals(), бинарное представление самого числа ведь должно совпадать с ZERO? Что ж там тогда отличаться будет?
Reply
Если оно не совпадает, закономерным образом объекты не равны.
При том, что представляют они одно и то же число ноль.
Reply
Два биг децимала равны. Но их разность не ноль.
Более того, разность числа с самим собой не ноль, а 0E-51.
Точнее, 0E-51 это ноль. Но не зеро.
System.out.println(BigDecimal.ZERO.compareTo(three)); // 0
System.out.println(BigDecimal.ZERO.equals(three)); // false
То есть, они не равны как объекты, но численно равны.
Что, в общем-то, логично.
Reply
Так вы ж только что писали, что "Я в курсе и причин и следствий", а "тут" первый пример из поста, просто развёрнутый на несколько строчек :-) Следствия более-менее ясны, а вот причины я как-то пока не понимаю.
> То есть, они не равны как объекты, но численно равны.
compareTo() опять-таки может иметь допуск в последних одном-двух битах мантиссы, как раз чтобы его можно было относительно безопасно использовать в вычислениях, так что compareTo()==0 ещё не означает что они вот прямо совсем-совсем равны, они могут отличаться.
А вот механизм этого, повторюсь, я не понимаю.
Reply
Поэтому для него 0.0 и 0.0000 равны. При том что это не идентичные объекты.
Reply
Что-то смахивающее на "ненормализованный ноль". Тоже упомянутый у Кнута. И смысл - что вплоть до 10-50 разность нулевая, а за ещё меньшие разряды не отвечаем...
Reply
Leave a comment