Проблема здесь не в вызове 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 уже! Но это не вредит.)