被 DecimalFormat/RoundingMode 搞糊涂了。关闭边缘情况的功能

Confused by DecimalFormat/RoundingMode.Down functionality for edge cases

基于 ,我希望以下打印出 0.59 而不是 0.60。

import java.math.RoundingMode;
import java.text.DecimalFormat;

public class Test {
    public static void main(String[] args) {
        double toFormat = 0.6;
        DecimalFormat formatter = new DecimalFormat("########0.00");
        formatter.setRoundingMode(RoundingMode.DOWN);
        System.out.println(formatter.format(toFormat)); // 0.60
    }
}

最接近0.60的浮点表示是0.59999999999999997779553950749686919152736663818359375,低于0.6。在 Java 8 中将 DecimalFormat 设置为 RoundingMode.DOWN,为什么不向下舍入为 0.59?

因为格式化代码知道 double.

的精度

请参阅 source code 中方法 shouldRoundUp(...) 中 class 中的评论 java.text.DigitList:

To avoid erroneous double-rounding or truncation when converting a binary double value to text, information about the exactness of the conversion result in FloatingDecimal, as well as any rounding done, is needed in this class.

  • For the HALF_DOWN, HALF_EVEN, HALF_UP rounding rules below: In the case of formating float or double, We must take into account what FloatingDecimal has done in the binary to decimal conversion.

    Considering the tie cases, FloatingDecimal may round-up the value (returning decimal digits equal to tie when it is below), or "truncate" the value to the tie while value is above it, or provide the exact decimal digits when the binary value can be converted exactly to its decimal representation given formating rules of FloatingDecimal ( we have thus an exact decimal representation of the binary value).

    • If the double binary value was converted exactly as a decimal value, then DigitList code must apply the expected rounding rule.

    • If FloatingDecimal already rounded up the decimal value, DigitList should neither round up the value again in any of the three rounding modes above.

    • If FloatingDecimal has truncated the decimal value to an ending '5' digit, DigitList should round up the value in all of the three rounding modes above.

    This has to be considered only if digit at maximumDigits index is exactly the last one in the set of digits, otherwise there are remaining digits after that position and we don't have to consider what FloatingDecimal did.

  • Other rounding modes are not impacted by these tie cases.

  • For other numbers that are always converted to exact digits (like BigInteger, Long, ...), the passed alreadyRounded boolean have to be set to false, and allDecimalDigits has to be set to true in the upper DigitList call stack, providing the right state for those situations..