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);