0

所以我有 BigDecimal 持有价值 0.99 我调用:

  • Float.toHexString(rationalNumber.floatValue()) 我得到0x1.fae148p-1
  • Double.toHexString(rationalNumber.doubleValue()) 我得到0x1.fae147ae147aep-1

我在想,因为我们代表的是一个像 0.99 这样的小数字,所以无论如何我们都应该得到相同的十六进制值。同意?

代码(未通过测试):

@Test public void testDelta() {
    BigDecimal rationalNumber = new BigDecimal("0.99").setScale(2,BigDecimal.ROUND_DOWN);

    String hexFromFloat = Float.toHexString(rationalNumber.floatValue());
    String hexFromDouble = Double.toHexString(rationalNumber.doubleValue());

    String hexFromFloatMsg = rationalNumber.floatValue() + " = " + hexFromFloat;
    String hexFromDoubleMsg = rationalNumber.doubleValue() + " = " + hexFromDouble;

    Assert.assertEquals(hexFromFloatMsg + ", " + hexFromDoubleMsg, hexFromDouble, hexFromFloat);
}

输出:

org.junit.ComparisonFailure: 0.99 = 0x1.fae148p-1, 0.99 = 0x1.fae147ae147aep-1 
Expected :0x1.fae147ae147aep-1
Actual   :0x1.fae148p-1
4

2 回答 2

2

不同之处在于这两个操作:

rationalNumber.floatValue()
rationalNumber.doubleValue()

其中每一个都将BigDecimal0.99 的值转换为浮点数。在十六进制浮点数中(使用十进制指数表示 2 的幂),.99 是 0x1.fae147ae147ae147…p-1。当它被转换为 时float,只能存储 24 位有效数(小数部分),因为这是该float类型对于有效数的所有位。(23 位是显式存储的;一个是编码的其他部分隐含的。)因此,转换必须将 .99 的确切值四舍五入到适合 24 位的值。这会产生 1.fae148p-1。如果你用二进制写 1.fae147ae147ae147... 并数出 24 位,你可以看到舍入发生的位置。它们的前 24 位以粗体显示:1.11111010111000010100011110101110000101000111…p-1。四舍五入时,我们查看被移除的位,发现它们超过了被保留的最低位的一半(被移除的第一位是 1,除此之外还有额外的 1 位),然后决定向上舍入。所以四舍五入产生 1.11111010111000010100100p-1。在十六进制中,即 1.fae148p-1。

当 .99 转换为 时double,可以存储 53 位有效位。所以它在不同的位置被四舍五入。这会产生 0x1.fae147ae147aep-1。

两个值不同,直接比较会报不同,从浮点格式转换为十六进制数字会产生不同的结果。

于 2013-10-26T15:36:40.613 回答
-1

因为我们代表的是一个像 0.99 这样的小数字,所以无论如何我们都应该得到相同的十六进制值。同意?

这是十进制的小精度,但是,如您所见,它的十六进制表示重复 ( 0x1.f(ae147)*)。它被准确地表示,但不能准确地打印出来

于 2013-10-26T15:02:02.400 回答