xyz*_*xyz 43 java floating-point numbers
我有以下代码:
Double i=17.31;
long j=(long) (i*100);
System.out.println(j);
Run Code Online (Sandbox Code Playgroud)
O/P: 1730 //Expected:1731
Double i=17.33;
long j=(long) (i*100);
System.out.println(j);
Run Code Online (Sandbox Code Playgroud)
O/P: 1732 //Expected:1733
Double i=17.32;
long j=(long) (i*100);
System.out.println(j);
Run Code Online (Sandbox Code Playgroud)
O/P: 1732 //Expected:1732{As expected}
Double i=15.33;
long j=(long) (i*100);
System.out.println(j);
Run Code Online (Sandbox Code Playgroud)
O/P: 1533 //Expected:1533{as Expected}
我曾试图谷歌,但无法找到理由.如果问题很简单,我很抱歉.
Ani*_*han 74
似乎没有任何答案可以解决为什么 17.32
采取不同的行动.
在你们之间看行为的差异17.32
和17.33 & 17.31
是由于IEEE-754 舍入规则.
应用舍入规则:来自Java™虚拟机规范 §2.8.1
Java虚拟机的舍入操作始终使用IEEE 754舍入到最近模式.不精确的结果四舍五入到最接近的可表示值,并且连接到具有零最低有效位的值.这是IEEE 754默认模式.Java虚拟机不提供任何更改浮点舍入模式的方法
Double是 :( 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
Run Code Online (Sandbox Code Playgroud)
17.31 :(尾数比较)
Actual: 1.00010100111101011100001010001111010111000010100011110...
Internal: 1.0001010011110101110000101000111101011100001010001111
Run Code Online (Sandbox Code Playgroud)
17.32 :(尾数比较)
Actual: 1.00010101000111101011100001010001111010111000010100011...
Internal: 1.0001010100011110101110000101000111101011100001010010 //round-up!
Run Code Online (Sandbox Code Playgroud)
17.33 :(尾数比较)
Actual: 1.00010101010001111010111000010100011110101110000101000...
Internal: 1.0001010101000111101011100001010001111010111000010100
Run Code Online (Sandbox Code Playgroud)
17.31 -> 17.309999999999998721023075631819665431976318359375...
17.32 -> 17.32000000000000028421709430404007434844970703125... //(was rounded up)
17.33 -> 17.3299999999999982946974341757595539093017578125...
Run Code Online (Sandbox Code Playgroud)
编辑:正如@Jeppe Stig Nielsen所说,在你的乘法步骤中有一个更重要的因素.FP乘法(参考)步骤的结果自己舍入到最近.这改变了哪些结果是预期的,哪些不是,但原因仍然与上述完全相同.
最后,由于强制转换(long)
,会发生截断,并为您留下您看到的结果.(1730, 1732, 1732)
缩小原始转换:Java™语言规范 §5.1.3
如果浮点数不是无穷大,浮点值将四舍五入为整数值V,使用IEEE 754舍入为零的模式舍入为零
svz*_*svz 22
该double
值不是17.31,而是17.309999999999999.这就是为什么当你乘以100得到1730.99999999999999999.转换为Long
您的double
值后会被截断为零.所以你得到1730.
克苏鲁和svz的答案是正确的。如果要将双精度数乘以 100 并避免浮点舍入错误,可以在每次乘法后将Math.round()
结果舍入到最接近的值:long
Double i=17.31;
long j=Math.round(i*100);
System.out.println(j);
Run Code Online (Sandbox Code Playgroud)
当处理极大(或负)双精度数时,这仍然会产生浮点错误。double 的绝对值越大,它与 Java 可以表示的下一个 double 之间的差异就越大。在某个点之后,连续的双精度数相距超过一个整数,并且传统的舍入将无法消除差异。不过,对于您发布的示例,这应该可行。
如已经解释的那样,这是由于非常小的浮点精度.
这可以通过使用Math.round()命令来解决,如下所示:
long j=Math.round(i*100);
Run Code Online (Sandbox Code Playgroud)
这将允许程序通过不使用floor操作来补偿使用浮点计算继承的非常小的错误,如默认(long)那样.