【问题标题】:Why is this number rounded down?为什么这个数字要四舍五入?
【发布时间】:2014-05-25 09:20:04
【问题描述】:

我对 Java 的 NumberFormat 的行为感到困惑。

考虑以下将双精度值转换为其百分比表示并将所得百分比四舍五入到小数点后三位的方法:

public static String doubleToPercent(final double val) {
    NumberFormat nf = NumberFormat.getPercentInstance();
    // Default rounding mode is HALF_EVEN
    nf.setRoundingMode(RoundingMode.HALF_UP);
    nf.setMaximumFractionDigits(3);
    return nf.format(val);
}

舍入模式为HALF_UP

worksAsExpected 中的结果并不让我感到惊讶;测试通过:

@Test
public void worksAsExpected() {
    double input = 1.234585;
    String expected = "123.459%";
    String output = doubleToPercent(input);
    assertEquals(expected, output);
}

但是这个呢:

@Test
public void surprise() {
    double input = 1.234535;
    String expected = "123.454%";
    String output = doubleToPercent(input);
    assertEquals(expected, output);
}

为什么这个测试失败了?为什么1.234535 向下取整,而1.234585 向上取整?

另一方面,如果最后一位数字是6,则该数字向上取整。

@Test
public void noSurprise() {
    double input = 1.234536;
    String expected = "123.454%";
    String output = doubleToPercent(input);
    assertEquals(expected, output);
}

这与double 的精度限制有关吗?我猜想1.234535 这样的数字完全在double 的能力范围内。

我在 Java 1.7.0_51 上运行 Windows 7 x86。

感谢任何见解。

【问题讨论】:

    标签: java double number-formatting


    【解决方案1】:

    1.234535 = 1.2345349999999999379696191681432537734508514404296875 作为双精度数,所以到 6 位时它会向下舍入到 1.23453。

    您可以使用我的十进制/二进制转换器exploringbinary.com/binary-converter 看看会发生什么。

    注意:与普通二进制数不同,二进制小数的精确十进制表示始终具有相同的位数:

    0.001 =
    0.125, 
    
    0.1101101 =
    0.8515625, 
    
    0.00001110101011 =
    0.05731201171875, 
    
    1.0011110000001010011111000101101011000100011100011011 =
    1.2345349999999999379696191681432537734508514404296875
    
    etc.
    

    如果您需要更多地控制小数位和四舍五入,则必须使用BigDecimal。请注意,最好计算您对数字执行的每个操作的舍入误差。 See this question for details.

    【讨论】:

    • 你有没有用工具来得到那个号码?
    • @Matthias Braun 是的。我可以使用我的十进制/二进制转换器(exploringbinary.com/binary-converter),它返回1.00111100000010100111100011100111010001100100110001000101001100010001,0001100101000101001100然后我把它放在二进制到十进制转换器中得到 1.2345349999999999379696191681432537734508514404296875。 (不过,还有其他方法——可能更容易——做到这一点。)
    • @Aaron Digulla 这是一些相当繁重的编辑。删除 cmets、将 cmets 移入答案、合并答案是否正常?
    • 不,但我的回答只是部分正确,你显然比我更了解这个问题,而且 cmets 中有很多有用的信息,我觉得我应该解决这个问题。这似乎是最好的解决办法。随意改进我的编辑:-)
    • 我的主要反对意见是关于最后一段(关于 BigDecimal)——我没有写它。你能把这归咎于谁吗?谢谢。
    【解决方案2】:

    数字使用双精度浮点格式 (http://en.wikipedia.org/wiki/Double-precision_floating-point_format) 存储。

    对于您遇到精度问题,我并不感到惊讶,不幸的是,Java 中的 double 并不是执行准确计算的非常可靠的方法。 这也是原因之一,因为对于数学模块,首选 Matlab 等其他工具。

    无论如何,在您的示例中,您可能会解决您的问题(希望)增加nf.setMaximumFractionDigits(<something bigger than 3>); 中数字的数字

    或者您可以根据需要切换到BigDecimalDecimalFormat

    【讨论】:

    • 谢谢。 DecimalFormat 也会产生不正确的结果。但我同意BigDecimal 是要走的路。
    • 我对关于 Matlab 的评论感到困惑。 Matlab 和 Java 都使用 IEEE 754 64 位二进制浮点来表示 double。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-12
    • 1970-01-01
    • 2021-09-21
    • 1970-01-01
    相关资源
    最近更新 更多