为什么两个浮点结果在名义上具有相同的值时会有所不同?

San*_*adi 7 java floating-point

我最近在阅读有关在内存中存储浮点值的内容.我写了一个小程序来测试我读过的内容.我注意到Java处理浮点值的方式有所不同.

public class Test
{
   public static void main(String args[])
   {
     double a = 0.90;
     System.out.println(a);
     System.out.println(2.00-1.10);
   }
 }
Run Code Online (Sandbox Code Playgroud)

以上程序正在打印

0.9
0.8999999999999999
Run Code Online (Sandbox Code Playgroud)

为什么这两个语句都没有打印相同的值?我知道一些浮动值无法准确表示.在这种情况下,两者都应该给出相同的价值.

Pet*_*rey 8

为什么这两个语句都没有打印相同的值?

结果不一样.

我知道一些浮动值无法准确表示.

因此,您应该假设操作的结果可能取决于您使用的值的表示错误量.

for (long l = 1; l <= 1e16; l *= 10) {
    double a = l + 2;
    double b = l + 1.1;
    System.out.println(a + " - " + b + " is " + (a - b));
}
Run Code Online (Sandbox Code Playgroud)

随着值变大,表示错误增加并且与0.9的结果相比变大

3.0 - 2.1 is 0.8999999999999999
12.0 - 11.1 is 0.9000000000000004
102.0 - 101.1 is 0.9000000000000057
1002.0 - 1001.1 is 0.8999999999999773
10002.0 - 10001.1 is 0.8999999999996362
100002.0 - 100001.1 is 0.8999999999941792
1000002.0 - 1000001.1 is 0.9000000000232831
1.0000002E7 - 1.00000011E7 is 0.900000000372529
1.00000002E8 - 1.000000011E8 is 0.9000000059604645
1.000000002E9 - 1.0000000011E9 is 0.8999999761581421
1.0000000002E10 - 1.00000000011E10 is 0.8999996185302734
1.00000000002E11 - 1.000000000011E11 is 0.899993896484375
1.000000000002E12 - 1.0000000000011E12 is 0.9000244140625
1.0000000000002E13 - 1.00000000000011E13 is 0.900390625
1.00000000000002E14 - 1.000000000000011E14 is 0.90625
1.000000000000002E15 - 1.0000000000000011E15 is 0.875
1.0000000000000002E16 - 1.0000000000000002E16 is 0.0
Run Code Online (Sandbox Code Playgroud)

并且在表示错误变得如此之大的主题上,您的操作什么都不做.

for (double d = 1; d < Double.MAX_VALUE; d *= 2) {
    if (d == d + 1) {
        System.out.println(d + " + 1 == " + (d + 1));
        break;
    }
}
for (double d = 1; d < Double.MAX_VALUE; d *= 2) {
    if (d == d - 1) {
        System.out.println(d + " - 1 == " + (d - 1));
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

版画

9.007199254740992E15 + 1 == 9.007199254740992E15
1.8014398509481984E16 - 1 == 1.8014398509481984E16
Run Code Online (Sandbox Code Playgroud)


Eri*_*hil 6

当"0.90"转换为double时,结果为.9加上一些小错误,e 0.因此a等于.9 + e 0.

当"1.10"转换为double时,结果为1.1加上一些小错误e 1,因此结果为1.1 + e 1.

这两个错误e 0和e 1通常彼此无关.简单地说,不同的十进制数与二进制浮点数的距离不同.评估时2.00-1.10,结果是2-(1.1 + e 1)=.9-e 1.所以你的一个数字是.9 + e 0,另一个是.9-e 1,没有理由期望它们是相同的.

(在这种情况下,e 0是.00000000000000002220446049250313080847263336181640625,e 1是.000000000000000088817841970012523233890533447265625.另外,在Sterbenz'Lemma将"1.1"转换为double之后,从2中减去1.1不会引入新的错误.)

额外细节:

在二进制文件中,.9是.11100110011001100110011001100110011001100110011001100 11001100 ...以粗体显示的位适合双精度.尾随位不适合,因此该数字在该点处舍入.这导致.9的精确值与表示为double的".9"的值之间存在差异.在二进制中,1.1是1.00011001100110011001100110011001100110011001 10011001 ...再次,数字是四舍五入的.但观察量舍入是不同的.对于.9,1100 1100 ...被舍入到1 0000 0000 ...,在该位置增加了00110011 .... 对于1.1,将1001 1001向上舍入为1 0000 0000 ...,在该位置添加01100110 ...(并以粗体位表示进位).这两个职位不同; 1.1从小数点的左边开始,所以它看起来像这样:1.[这里是52位 ] [ 发生舍入的地方 ]..9从小数点开始,所以它看起来像这样:.[ 53位 ] [ 舍入发生的地方 ].所以1.1的舍入,除了01100110 ...而不是00110011 ...,也加倍,因为它出现在.9舍入的左边一位.所以你有两个效果使得e 0与e 1不同:舍入的尾部位是不同的,并且舍入发生的位置是不同的.