BigDecimal с RoundingMode.FLOOR дает неожиданный результат - PullRequest
0 голосов
/ 28 января 2020

Я получаю неожиданный результат. Я хочу удалить цифры после двух десятичных знаков

BigDecimal(0.55).setScale(2, RoundingMode.FLOOR) //return 0.55
BigDecimal(0.56).setScale(2, RoundingMode.FLOOR) //return 0.56
BigDecimal(0.57).setScale(2, RoundingMode.FLOOR) //return 0.56
BigDecimal(**0.58**).setScale(2, RoundingMode.FLOOR) //return 0.57
BigDecimal(**0.59**).setScale(2, RoundingMode.FLOOR) //return 0.58
BigDecimal(**0.60**).setScale(2, RoundingMode.FLOOR) //return 0.59
BigDecimal(**0.61**).setScale(2, RoundingMode.FLOOR) //return 0.60
BigDecimal(**0.62**).setScale(2, RoundingMode.FLOOR) //return 0.61
BigDecimal(0.63).setScale(2, RoundingMode.FLOOR) //return 0.63

Ответы [ 2 ]

2 голосов
/ 28 января 2020

Проблема здесь не в вызове setScale(), а происходит до этого. Вы строите BigDecimal из значения Double - значения, которое близко к 0,58, но не совсем.

(FLoat s и Double s имеют значение двоичное с плавающей запятой - и почти никакие десятичные дроби не имеют точного двоичного представления . В этом случае 0,58 равно 0,10010100011110101110000101… в двоичном формате. Нет Double может хранить это точно; лучшее, что может сделать, - это хранить значение, очень близкое к нему.)

BigDecimal тогда делает все возможное, чтобы сохранить это значение как можно точнее. Вы можете увидеть это в REPL:

>>> java.math.BigDecimal(0.58)
res1: java.math.BigDecimal = 0.57999999999999996003197111349436454474925994873046875

… что делает очевидным, почему это округляется до 0,57, а не 0,58.

И это справедливо для всех ваших проблемных случаев: ближайший Double немного меньше указанного десятичного значения и поэтому округляется. (В остальных случаях он немного больше , поэтому они округляются, как и ожидалось. Но основная проблема все еще существует.)

Лучшее решение - полностью избежать двоичных чисел с плавающей точкой, и использовать конструктор BigDecimal(String) :

>>> java.math.BigDecimal("0.58").setScale(2, java.math.RoundingMode.FLOOR)
res6: java.math.BigDecimal! = 0.58

(В этом случае, конечно, больше нет необходимости в вызове setScale(), поскольку значение теперь имеет масштаб 2 уже! Но это не вредит.)

2 голосов
/ 28 января 2020

Вы стали жертвой двойной точности с плавающей запятой. Когда вы пишете

0.62

в действительности, это на самом деле что-то вроде

0.6199999999999626787363666....

Вы должны использовать BigDecimal конструктор, который принимает String вместо Double аргумент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...