Java 双倍乘以100,然后转换为long,给出的值是错误的
我有以下代码:Java 双倍乘以100,然后转换为long,给出的值是错误的,java,floating-point,numbers,Java,Floating Point,Numbers,我有以下代码: Double i=17.31; long j=(long) (i*100); System.out.println(j); O/p:1730//预计:1731 Double i=17.33; long j=(long) (i*100); System.out.println(j); O/p:1732//预计:1733 Double i=17.32; long j=(long) (i*100); System.out.println(j); O/p:1732//预期:1732
Double i=17.31;
long j=(long) (i*100);
System.out.println(j);
O/p:1730//预计:1731
Double i=17.33;
long j=(long) (i*100);
System.out.println(j);
O/p:1732//预计:1733
Double i=17.32;
long j=(long) (i*100);
System.out.println(j);
O/p:1732//预期:1732{As Expected}
Double i=15.33;
long j=(long) (i*100);
System.out.println(j);
O/p:1533//预期:1533{as Expected}
Double i=15.33;
long j=(long) (i*100);
System.out.println(j);
我试着用谷歌搜索,但找不到原因。如果这个问题很琐碎,我很抱歉 双精度
值不表示为17.31,而是表示为17.309999999999。这就是为什么当你把它乘以100,你会得到1730.999999999999。转换为Long
后,您的double
值被截断为零。所以你得到了1730。这与内部表示有关。如果你看第一个例子中的i*100,你会发现它是1730.999999998。铸件将仅移除点(截断)后的零件。进行这种长转换时,它是地板。你的17.31实际上可能是17.309999999,这就是为什么它导致1730而不是1731
使用i=i*100,则i.longValue()将解决问题。所有答案似乎都无法解释为什么17.32不同
1.为什么会这样
您在17.32
和17.33和17.31
之间看到的行为差异是由于IEEE-754舍入规则造成的
适用的舍入规则:从§2.8.1开始
Java虚拟机的舍入操作始终使用IEEE
754四舍五入到最近的模式。不精确的结果四舍五入到最接近的值
可表示的值,与带零的值关联
最低有效位。这是IEEE 754默认模式。Java虚拟机
机器不提供任何改变浮点舍入的方法
模式
2.你的情况:
双精度是:(1符号位+11指数位+52分数位=64位)。以下四舍五入后的内部表示:
3.内部陈述(证明):
17.31:(尾数比较)
17.32:(尾数比较)
17.33:(尾数比较)
4.转换回十进制:
()
5.长
编辑:正如@Jeppe Stig Nielsen所说,在乘法步骤中有一个更重要的因素。FP乘法()步骤的结果向最近的方向舍入。
这改变了预期的结果和预期的结果,但原因仍然与上述完全相同
最后,由于强制转换(long)
,会发生截断,并留下您看到的结果<代码>(173017321732)
缩小原语转换:§5.1.3
如果浮点数不是无穷大,则浮点数
值四舍五入为整数值V,使用
IEEE 754向零模式进发
如前所述,这是由于浮点精度非常小 这可以通过使用Math.round()命令解决,如下所示:
long j=Math.round(i*100);
这将允许程序通过不使用floor操作来补偿使用浮点计算继承的非常小的错误,就像默认值(long)那样。Cthulhu和svz的答案是正确的。如果要将double乘以100并避免浮点舍入错误,可以在每次乘法后使用
Math.round()
将结果舍入到最接近的long
:
Double i=17.31;
long j=Math.round(i*100);
System.out.println(j);
在处理非常大(或负)的双精度运算时,仍然会有浮点错误。double的绝对值越大,它与Java所能表示的下一个double之间的差异就越大。在某一点之后,连续的双精度数之间的距离超过一个整数,传统的舍入将无法消除差异。对于您发布的示例,这应该是可行的。对于值,
字符串值=1074.6
使用下面的代码
double amount=double.parseDouble(值)代码>
String roundedValue=String.valueOf(Math.round(amount*100))
您可以得到结果107460不确定,因此有评论,但这可能是由于舍入问题。如果是这样的话,前面的SO线程应该会有所帮助。该类应该有助于减少这些错误。为什么人们每次迭代都会大量更新“Q:‘浮点古怪’A:‘IEEE-754’”,而不是标记/投票以完全重复的形式关闭?作为一种习惯,如果您想将十进制数转换为整数,并且要使用截断函数,请在截断之前添加.5。它将为您提供更好的四舍五入(1.75将四舍五入为2而不是1),但最重要的是将避免此问题。@DanNeely我查找了一个副本,但找不到一个精确解决四舍五入问题的副本。每个人都链接到一本书,这本书本身就足够长了指向我在打字时睡着了,醒来前发了帖子。I.longValue
的行为方式与(long)I
完全相同。17.32不是存储为17.31999999999999吗?它可能是17.31999999999999或17.32000000000001。这种问题是编程界的首要问题。准确地说,它表示为17.309999999998721023075631819665431976318359375。准确地说,它根本不是十进制,而是二进制。像17.3125这样的数字有精确的二进制表示,因为它是17+5/16,但17.31没有精确的二进制表示。你不能把它表示为二次幂的有限和。这也是因为这个链接到了nice工具。但是:记住,100.0
的乘法会导致进一步的舍入。考虑案例<代码> 17.34 /代码>。此数字的双精度表示也必须向下舍入。但在乘以100.0
之后,它仍然给出了预期的输出!原因是乘法算法的一部分是为了得到整数
Actual: 1.00010101010001111010111000010100011110101110000101000...
Internal: 1.0001010101000111101011100001010001111010111000010100
17.31 -> 17.309999999999998721023075631819665431976318359375...
17.32 -> 17.32000000000000028421709430404007434844970703125... //(was rounded up)
17.33 -> 17.3299999999999982946974341757595539093017578125...
long j=Math.round(i*100);
Double i=17.31;
long j=Math.round(i*100);
System.out.println(j);