如何在Java中避免双精度丢失?

时间:2017-08-03 19:07:21

标签: java precision

我正在编写一个程序,它从数据库中获取原始double值并将它们转换为8字节的十六进制字符串,但我不知道如何防止精度损失。从所有设备接收的数据存储为双精度数,包括8字节标识值。

7.2340172821234e + 16等双精度实例在不损失精度的情况下正确解析,指数为10 ^ 16。

但是,在指数为10 ^ 17的情况下,Java会失去精度。 例如,2.88512954935019e + 17被Java解释为1.44464854248327008E17

我使用的代码如下所示:

public Foo(double bar) {
    this.barString = Long.toHexString((long) bar);
    if (barString.length == 15) {
        barString = "0" + barString; //to account for leading zeroes lost on data entry
    }
}

我正在使用与此类似的测试用例来测试它:

@Test
public void testFooConstructor() {
    OtherClass other = new OtherClass();

    OtherClass.Foo test0 = other.new Foo(72340172821234000d); //7.2340172821234e+16
    assertEquals("0101010100000150", test0.barString); //This test passes

    OtherClass.Foo test1 = other.new Foo(144464854248327000d);//1.44464854248327e+17
    assertEquals("02013e0500000758, test1.barString); //This test fails
}

单元测试表明:

Expected: 02013e0500000758
Actual:   02013e0500000760

当我打印出Java存储的值72340172821234000d和144464854248327000d分别打印时:

  

7.2340172821234E16

     

1.44464854248327008E17

后一个值偏离8,这似乎与我测试的少数几个一致。

我有什么办法可以纠正这个错误吗?

编辑:这不是一个问题,我关心过去的地方。一些人认为这是重复的问题是,为什么浮点数不那么准确,我问的是如何避免精度的损失,通过与Roman Puchkovskiy建议的那些类似的变通方法。

2 个答案:

答案 0 :(得分:2)

您可以将数据库中的浮点值作为字符串(而不是浮点数),然后使用BigDecimal将它们转换为long

String fpAsString = getFromDB();
long longValue = new BigDecimal(fpAsString).longValue();
this.barString = Long.toHexString(longValue);

BigDecimal。longValue()类似于缩小从doublelong的原始转换,但它不会失去精度(除了丢失小数部分)。如果结果不适合long,您可能会丢失一些内容,但是对于long的广告投放会发生同样的情况。

答案 1 :(得分:1)

Float和Double类型是非常适合存储非常大数字或非常小数字的变量,但是存储具有大量数字的数字非常糟糕,这是由于它们的二进制表示。

基本上,如果看一下Double或Float如何存储在内存中,那么符号有一位,指数有几位,小数位有几位。

因此,当查看值实际存储在内存中时,它是这样的:

bits

实际值的计算如下:

formula

(此示例引用以32位表示的Float,双位用64位表示,但适用相同的原则)

数字可以表示的位数限制为小数部分可以表示的位数,但即使数字位数非常有限,双数和浮点数也可以通过使用指数表示非常大的数字和非常小的数字。

在java Double中,小数部分取52位,如果你要在计算器中检查52位数可以是最大数字(formula2),你会看到你得到一个16位数字。 Double可以通过在使用指数所代表的数字之前或之后添加零来表示更大的数字,但是它不能存储具有超过16位数而不会丢失精度的数字。

注意,实际上还有更多内容,这只是Double和Float表示的一个非常基本的解释。 如果您想深入了解更准确的解释,可以查看此维基百科页面:https://en.wikipedia.org/wiki/Single-precision_floating-point_format