java 双倍乘以 100 然后转换为 long 给出错误的值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13467849/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Double multiplied by 100 and then cast to long is giving wrong value
提问by xyz
I have the following code:
我有以下代码:
Double i=17.31;
long j=(long) (i*100);
System.out.println(j);
O/P : 1730 //Expected:1731
开/关: 1730 //Expected:1731
Double i=17.33;
long j=(long) (i*100);
System.out.println(j);
O/P : 1732 //Expected:1733
开/关: 1732 //Expected:1733
Double i=17.32;
long j=(long) (i*100);
System.out.println(j);
O/P : 1732 //Expected:1732{As expected}
开/关: 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}
开/关: 1533 //Expected:1533{as Expected}
I have tried to Google but unable to find reason.I am sorry if the question is trivial.
我试过谷歌,但找不到原因。如果问题是微不足道的,我很抱歉。
回答by Anirudh Ramanathan
None of the answers seem to deal with why17.32
acted different.
似乎没有一个答案涉及为什么17.32
行为不同。
1. Why it occurred
1. 为什么会发生
The difference in behaviour you see between 17.32
and 17.33 & 17.31
is due to IEEE-754 Roundingrules.
您在17.32
和之间看到的行为差异17.33 & 17.31
是由于 IEEE-754舍入规则造成的。
Rounding rule applied: from, The Java? Virtual Machine Specification§2.8.1
应用的舍入规则:来自,Java?虚拟机规范§2.8.1
The rounding operations of the Java virtual machine always use IEEE 754 round to nearest mode. Inexact results are rounded to the nearest representable value, with ties going to the value with a zero least-significant bit. This is the IEEE 754 default mode. The Java virtual machine does not give any means to change the floating-point rounding mode
Java 虚拟机的舍入操作始终使用 IEEE 754 舍入到最近模式。不精确的结果被四舍五入到最接近的可表示值,关系到最低有效位为零的值。这是 IEEE 754 默认模式。Java虚拟机没有给出任何改变浮点舍入方式的方法
2. Your case:
2. 你的情况:
Double is: (1 sign-bit + 11 exponent-bits + 52 fraction-bits = 64bits). Internal representation after roundingbelow:
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
3. Internal representation (Proof):
3. 内部表示(Proof):
17.31: (Mantissa comparison)
17.31:(尾数比较)
Actual: 1.00010100111101011100001010001111010111000010100011110...
Internal: 1.0001010011110101110000101000111101011100001010001111
17.32: (Mantissa comparison)
17.32:(尾数比较)
Actual: 1.00010101000111101011100001010001111010111000010100011...
Internal: 1.0001010100011110101110000101000111101011100001010010 //round-up!
17.33: (Mantissa comparison)
17.33:(尾数比较)
Actual: 1.00010101010001111010111000010100011110101110000101000...
Internal: 1.0001010101000111101011100001010001111010111000010100
4. Conversion back-to-decimal:
4. 转换回十进制:
17.31 -> 17.309999999999998721023075631819665431976318359375...
17.32 -> 17.32000000000000028421709430404007434844970703125... //(was rounded up)
17.33 -> 17.3299999999999982946974341757595539093017578125...
5. Cast to long
5.投到长
EDIT:There is a factor more at play at your multiplication step as @Jeppe Stig Nielsen said. The result of the FPmultiplication (Reference) step does its own rounding-towards-nearest. This changes which results are as expected and which aren't, but the reason is still exactly the same as stated above.
编辑:正如@Jeppe Stig Nielsen 所说,在乘法步骤中还有一个因素在起作用。所述的结果FP乘法(参考)步骤执行其自己的舍入入,向最近的。这会改变哪些结果符合预期,哪些不符合预期,但原因仍然与上述完全相同。
Finally, due to the cast (long)
, truncation occurs, and leaves you with the results you see. (1730, 1732, 1732)
最后,由于 cast (long)
,会发生截断,并留下您看到的结果。(1730, 1732, 1732)
Narrowing Primitive Conversion : The Java? Language Specification§5.1.3
缩小原始转换:Java?语言规范§5.1.3
If the floating-point number is not an infinity, the floating-point value is rounded to an integer value V, rounding toward zero using IEEE 754 round-toward-zero mode
如果浮点数不是无穷大,则将浮点值舍入为整数值 V,使用 IEEE 754 向零舍入模式向零舍入
回答by svz
The double
value is represented not as 17.31, but as 17.309999999999999. That's why when you multiply it by 100 you get 1730.99999999999999999. After conversion to Long
your double
value is truncated towards zero. So you get 1730.
该double
值不表示为 17.31,而是表示为 17.309999999999999。这就是为什么当你将它乘以 100 时,你会得到 1730.99999999999999999。转换为Long
您的double
值后会被截断为零。所以你得到1730。
回答by PearsonArtPhoto
As has been explained, this is due to very small floating point precision.
如前所述,这是由于非常小的浮点精度。
This can be resolve via using a Math.round(), command, as follows:
这可以通过使用 Math.round() 命令来解决,如下所示:
long j=Math.round(i*100);
This will allow the program to compensate for the very small errors which are inherit using floating point calculations, by not using a floor operation, as the default (long) does.
这将允许程序通过不使用地板运算来补偿使用浮点计算继承的非常小的错误,就像默认值 (long) 那样。
回答by Burkhard
It has to do with the internal representation. If you take a look at i*100 in the first case, you'll see that it is 1730.9999999999998. The cast will only remove the part after the point (truncated).
它与内部表示有关。如果您查看第一种情况中的 i*100,您会看到它是 1730.9999999999998。演员只会移除点之后的部分(被截断)。
回答by Kevin
Cthulhu and svz's answers are correct. If you want to multiply doubles by 100 and avoid floating point rounding errors, you can use Math.round()
to round the result to the closest long
after each multiplication:
Cthulhu 和 svz 的答案是正确的。如果您想将 double 乘以 100 并避免浮点舍入错误,您可以使用在每次乘法后将Math.round()
结果舍入到最接近的值long
:
Double i=17.31;
long j=Math.round(i*100);
System.out.println(j);
This will still have floating point error when dealing extremely large (or negative) doubles. The larger the absolute value of a double, the more the difference is between it and the next double that Java can represent. After some point, consecutive doubles are more than an integer apart, and conventional rounding won't be able to smooth out the difference. For the examples you posted, this should work, though.
在处理非常大(或负)的双打时,这仍然会出现浮点错误。double 的绝对值越大,它与 Java 可以表示的下一个 double 之间的差异就越大。在某个时间点之后,连续的双打相隔超过一个整数,传统的舍入将无法消除差异。不过,对于您发布的示例,这应该可行。
回答by Gang Su
When you do this kind of long conversion it's floor. Your 17.31 could actually be 17.30999999999 and that's why it resulted in 1730 instead of 1731.
当你做这种长时间的转换时,它是地板。您的 17.31 实际上可能是 17.30999999999,这就是为什么结果是 1730 而不是 1731。
use i = i * 100, then i.longValue() will solve the problem.
使用 i = i * 100,那么 i.longValue() 将解决问题。