加倍乘以 100,然后转换为多头是给出错误的值

2022-08-31 20:24:56

我有以下代码:

Double i=17.31;
long j=(long) (i*100);
System.out.println(j);

操作 :1730 //Expected:1731

Double i=17.33;
long j=(long) (i*100);
System.out.println(j);

操作 :1732 //Expected:1733

Double i=17.32;
long j=(long) (i*100);
System.out.println(j);

操作 :1732 //Expected:1732{As expected}

Double i=15.33;
long j=(long) (i*100);
System.out.println(j);

操作 :1533 //Expected:1533{as Expected}

我试图谷歌,但找不到原因。如果这个问题是微不足道的,我很抱歉。


答案 1

似乎没有一个答案涉及为什么行为不同。17.32

1. 为什么会发生

您看到的 和 之间的行为差异是由于 IEEE-754 舍入规则造成的。17.3217.33 & 17.31

应用的舍入规则:来自 Java™ 虚拟机规范 §2.8.1

Java 虚拟机的舍入操作始终使用 IEEE 754 舍入到最接近模式。不精确的结果将舍入到最接近的可表示值,并且关系将转到具有零最低有效位的值。这是 IEEE 754 默认模式。Java 虚拟机不提供任何更改浮点舍入方式的方法


2. 您的案例:

双精度为:(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. 内部代表(证明):

17.31: (尾狮比较)

Actual:   1.00010100111101011100001010001111010111000010100011110...
Internal: 1.0001010011110101110000101000111101011100001010001111

17.32: (尾数比较)

Actual:   1.00010101000111101011100001010001111010111000010100011... 
Internal: 1.0001010100011110101110000101000111101011100001010010    //round-up!

17.33: (尾数比较)

Actual:   1.00010101010001111010111000010100011110101110000101000...
Internal: 1.0001010101000111101011100001010001111010111000010100

4. 转换回十进制:

17.31 ->  17.309999999999998721023075631819665431976318359375...
17.32 ->  17.32000000000000028421709430404007434844970703125... //(was rounded up)
17.33 ->  17.3299999999999982946974341757595539093017578125...

(IEEE-754 分析工具)

5. 投长

编辑:在你的乘法步骤中,有一个因素在起作用,正如@Jeppe斯蒂格·尼尔森(Stig Nielsen)所说。FP 乘法(参考)步骤的结果执行自己的舍入-向最接近。这改变了哪些结果符合预期,哪些结果不符合预期,但原因仍然与上述完全相同。

最后,由于施法 ,截断发生,并留下您看到的结果。(long)(1730, 1732, 1732)

缩小原始转换范围:Java™ 语言规范 §5.1.3

如果浮点数不是无穷大,则浮点值将舍入为整数值 V,并使用 IEEE 754 向零舍入模式舍入为零


答案 2

该值不表示为 17.31,而是表示为 17.30999999999999999。这就是为什么当你乘以100时,你得到1730.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999转换为您的值后,将截断为零。所以你得到1730。doubleLongdouble