双倍乘以100然后转换为long则给出错误的值

时间:2012-11-20 06:41:05

标签: java floating-point numbers

我有以下代码:

Double i=17.31;
long j=(long) (i*100);
System.out.println(j);

O / P:1730 //Expected:1731

Double i=17.33;
long j=(long) (i*100);
System.out.println(j);

O / P:1732 //Expected:1733

Double i=17.32;
long j=(long) (i*100);
System.out.println(j);

O / P:1732 //Expected:1732{As expected}

Double i=15.33;
long j=(long) (i*100);
System.out.println(j);

O / P:1533 //Expected:1533{as Expected}

我曾尝试过谷歌但无法找到原因。如果问题很简单,我很抱歉。

7 个答案:

答案 0 :(得分:73)

这些答案似乎都没有解决为什么 17.32采取的不同行为。

1。为什么会发生

您在17.3217.33 & 17.31之间看到的行为差异是由于IEEE-754 舍入规则造成的。

应用了舍入规则:来自,The Java™ Virtual Machine Specification §2.8.1

  

Java虚拟机的舍入操作始终使用IEEE   754轮到最近的模式。不精确的结果四舍五入到最近   可表示的值,关系将以零值表示   最不重要的一点。这是IEEE 754默认模式。 Java虚拟   机器不提供任何改变浮点舍入的方法   模式


2。你的情况:

Double is :( 1个符号位+ 11个指数位+ 52个小数位= 64位)。 以下四舍五入后的内部表示:

             1 [63]      11 [62-52]           52 [51-00]
              Sign        Exponent             Fraction

17.31 -->    0 (+)       10000000011 (+4)     1.0001010011110101110000101000111101011100001010001111
17.32 -->    0 (+)       10000000011 (+4)     1.0001010100011110101110000101000111101011100001010010 //rounded up
17.33 -->    0 (+)       10000000011 (+4)     1.0001010101000111101011100001010001111010111000010100

3。内部代表(证明):

17.31 :(尾数比较)

Actual:   1.00010100111101011100001010001111010111000010100011110...
Internal: 1.0001010011110101110000101000111101011100001010001111

17.32 :(尾数比较)

Actual:   1.00010101000111101011100001010001111010111000010100011... 
Internal: 1.0001010100011110101110000101000111101011100001010010    //round-up!

17.33 :(尾数比较)

Actual:   1.00010101010001111010111000010100011110101110000101000...
Internal: 1.0001010101000111101011100001010001111010111000010100

4。转换回小数:

17.31 ->  17.309999999999998721023075631819665431976318359375...
17.32 ->  17.32000000000000028421709430404007434844970703125... //(was rounded up)
17.33 ->  17.3299999999999982946974341757595539093017578125...

(的 IEEE-754 Analysis Tool

5。施展到长

编辑:正如@Jeppe Stig Nielsen所说,在你的乘法步骤中有一个更重要的因素。 FP 乘法( Reference )步骤的结果会自行舍入到最近。 这改变了哪些结果是预期的,哪些不是,但原因仍然与上述完全相同。

最后,由于强制转换(long),会发生截断,并为您留下您看到的结果。 (1730, 1732, 1732)

缩小原始转换:The Java™ Language Specification §5.1.3

  

如果浮点数不是无穷大,则为浮点数   value四舍五入为整数值V,使用舍入为零   IEEE 754向零舍入模式

答案 1 :(得分:21)

double值不是17.31,而是17.309999999999999。这就是为什么当你乘以100得到1730.99999999999999999。转换为Long后,您的double值会被截断为零。所以你得到1730。

答案 2 :(得分:5)

正如已经解释过的,这是由于浮点精度非常小。

这可以通过使用Math.round()命令解决,如下所示:

long j=Math.round(i*100);

这将允许程序通过不使用floor操作来补偿使用浮点计算继承的非常小的错误,如默认值(long)那样。

答案 3 :(得分:4)

它与内部表示有关。如果您在第一种情况下看一下i * 100,您会看到它是1730.9999999999998。演员表只会删除该点之后的部分(截断)。

答案 4 :(得分:3)

克苏鲁和斯维兹的答案是正确的。如果要将双精度乘以100并避免浮点舍入误差,可以使用Math.round()将结果舍入到每次乘法后最接近的long

Double i=17.31;
long j=Math.round(i*100);
System.out.println(j);

处理极大(或负)双打时仍会出现浮点错误。 double的绝对值越大,它与Java可以表示的下一个double之间的差异就越大。在一些点之后,连续的双倍超过整数,并且传统的舍入将无法消除差异。对于您发布的示例,这应该可行。

答案 5 :(得分:0)

当你进行这种长时间的转换时,它就是地板。你的17.31实际上可能是17.30999999999,这就是1730而不是1731的结果。

使用i = i * 100,然后i.longValue()将解决问题。

答案 6 :(得分:0)

对于像这样的价值, 字符串值 = 1074.6

使用下面的代码

double amount = Double.parseDouble(value);

String roundedValue = String.valueOf(Math.round(amount * 100))

你可以得到结果107460