Какие способы округления поддерживает BigDecimal?
Что проверяют
Знаете ли вы режимы RoundingMode и, главное, как они ведут себя на отрицательных числах, где путаются чаще всего. Следом обычно спрашивают, почему divide бросает исключение и какой режим брать для денег.
Как отвечать
Режимов восемь, все в перечислении java.math.RoundingMode. Удобно разложить их на группы:
UPиDOWN: от нуля и к нулю.2.1вUPстанет 3, а-2.1станет-3.DOWNпросто отбрасывает лишние знаки;CEILINGиFLOOR: к плюс и минус бесконечности.-2.1вCEILINGдаёт-2, воFLOORдаёт-3;HALF_UP,HALF_DOWN,HALF_EVEN: к ближайшему, различаются только на ровной половине.HALF_UPуводит2.5в 3,HALF_DOWNв 2,HALF_EVENк чётному:2.5в 2,3.5в 4. Последний называют банковским, он не даёт систематического смещения при суммировании множества округлений;UNNECESSARY: утверждение, что округлять не нужно. Если оно ложно, летитArithmeticException.
Применяется режим в setScale(2, RoundingMode.HALF_UP), где задаётся число знаков после запятой, или через MathContext, где задаётся общее число значащих цифр: new MathContext(5, RoundingMode.HALF_EVEN). Конструктор MathContext(int) берёт HALF_UP, а готовый MathContext.DECIMAL128 использует HALF_EVEN.
Целочисленные константы вроде BigDecimal.ROUND_HALF_UP устарели с Java 9, в новом коде только RoundingMode.
Где спотыкаются
Вызывают a.divide(b) без режима. Если частное бесконечная дробь, как 1/3, метод бросает ArithmeticException. Нужно divide(b, 2, RoundingMode.HALF_EVEN) или MathContext. То же с setScale(2) без режима: он ведёт себя как UNNECESSARY.
Создают число из double: new BigDecimal(0.1) хранит двоичную погрешность целиком. Правильно new BigDecimal("0.1") или BigDecimal.valueOf(0.1).
Сравнивают через equals. Для него 2.0 и 2.00 разные, потому что различается масштаб. Суммы сравнивают через compareTo.