我有以下代码:
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}
我曾尝试过谷歌但无法找到原因。如果问题很简单,我很抱歉。
答案 0 :(得分:73)
这些答案似乎都没有解决为什么 17.32
采取的不同行为。
您在17.32
和17.33 & 17.31
之间看到的行为差异是由于IEEE-754 舍入规则造成的。
应用了舍入规则:来自,The Java™ Virtual Machine Specification §2.8.1
Java虚拟机的舍入操作始终使用IEEE 754轮到最近的模式。不精确的结果四舍五入到最近 可表示的值,关系将以零值表示 最不重要的一点。这是IEEE 754默认模式。 Java虚拟 机器不提供任何改变浮点舍入的方法 模式
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
Actual: 1.00010100111101011100001010001111010111000010100011110...
Internal: 1.0001010011110101110000101000111101011100001010001111
17.32 :(尾数比较)
Actual: 1.00010101000111101011100001010001111010111000010100011...
Internal: 1.0001010100011110101110000101000111101011100001010010 //round-up!
17.33 :(尾数比较)
Actual: 1.00010101010001111010111000010100011110101110000101000...
Internal: 1.0001010101000111101011100001010001111010111000010100
17.31 -> 17.309999999999998721023075631819665431976318359375...
17.32 -> 17.32000000000000028421709430404007434844970703125... //(was rounded up)
17.33 -> 17.3299999999999982946974341757595539093017578125...
(的 IEEE-754 Analysis Tool 强>)
编辑:正如@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