Java 当两个浮点结果名义上具有相同的值时,为什么它们会不同?

Java 当两个浮点结果名义上具有相同的值时,为什么它们会不同?,java,floating-point,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); } } 上面的程序正在打印 0.9 0.89999999999

我最近读了一篇关于在内存中存储浮点值的文章。我写了一个小程序来测试我读到的东西。我注意到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);
   }
 }
上面的程序正在打印

0.9
0.8999999999999999
为什么这两条语句不打印相同的值?我知道有些浮点值不能精确表示。在这种情况下,两者应给出相同的值

为什么这两条语句不打印相同的值

结果不一样

我知道有些浮点值不能精确表示

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

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

当表示错误变得如此之大时,您的操作什么也不做

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

您的推理是,即使0.9不能用
double
精确表示,它也应该具有与2.0-1.1完全相同的
double
值,从而产生相同的打印值。这就是错误——这个减法不会产生由“0.9”(或精确值0.9)表示的
双精度

我知道有些浮点值不能精确表示

这就是你的答案(或者更准确地说,正如马克·拜尔斯所指出的,有些十进制值不能精确地表示为双精度)!0.9或1.1都不能表示为双精度,因此会出现舍入误差

您可以使用BigDecimal检查各种双精度的精确值:

public static void main(String args[]) {
    double a = 0.9d;
    System.out.println(a);
    System.out.println(new BigDecimal(a));
    double b = 2d - 1.1d;
    System.out.println(b);
    System.out.println(new BigDecimal(2.0d));
    System.out.println(new BigDecimal(1.1d));
    System.out.println(new BigDecimal(b));
}
哪些产出:

0.9
0.90000000000000002220446049250313080847263336181640625
0.8999999999999999
2
1.100000000000000088817841970012523233890533447265625
0.899999999999999911182158029987476766109466552734375

将“0.90”转换为双精度时,结果为.9加上一些小误差e0。因此
a
等于.9+e0

将“1.10”转换为双精度时,结果为1.1加上一些小误差e1,因此结果为1.1+e1

这两个错误e0和e1通常互不相关。简单地说,不同的十进制数与二进制浮点数的距离不同。计算
2.00-1.10
时,结果为2–(1.1+e1)=.9-e1。所以你的一个数字是.9+e0,另一个是.9-e1,没有理由期望它们是一样的

(在本例中,e0为.0000000000000000 2220446049250313080847263336181640625,e1为.0000000000000000 88817841970012523233890533447265625。此外,通过Sterbenz引理将“1.1”转换为双精度后,从2中减去1.1不会产生新的错误。)

其他详情:


在二进制中,.9是.1110011001100110011001100…在粗体中的位适合双精度。尾随位不匹配,因此数字在该点舍入。这会导致.9的精确值与表示为双精度的.9的值之间存在差异。在二进制中,1.1是1.0001100110011001…数字再次四舍五入。但观察四舍五入的数量是不同的。对于.9,1100…被四舍五入为1 0000 0000…,在该位置加上00110011。对于1.1,1001 1001被四舍五入到1 0000 0000…,这将在该位置添加01100110…(并导致粗体位的进位)。两种立场不同,;1.1从基数点的左边开始,看起来是这样的:1。[此处52位][发生舍入的位置]。9从小数点的右边开始,所以看起来像这样:[53位这里][发生舍入的地方]。因此,1.1的四舍五入除了是01100110…而不是00110011…,还加倍了,因为它发生在.9四舍五入的左边一位。因此,有两种效果使e0与e1不同:舍入的尾随位不同,舍入发生的位置也不同。

我喜欢结果收敛到0。;)是
aLot==aLot+1
。。。和
aLotLot==aLotLot+aLot
;-)也许选民不喜欢我的回答或语气?!该问题被不恰当地关闭,因为它不是该问题的完全副本,而该问题被标记为该问题的完全副本。所谓的原版询问浮点表示的不精确性,但这个问题问为什么具有相同数学结果的两个计算不具有相同的计算结果。浮点行为的这一特性不同于浮点无法表示十进制数字。这里失去了一个解释浮点运算原理的机会。@EricPostchil我不同意。这个问题是关于float vs double的,其中一个答案引用了IEEE754,这就是这个问题的答案。@EJP:“IEEE754”不是这个问题的答案。用“IEEE 754”或规范链接回答所有关于IEEE 754浮点的问题是没有意义的。回答一个问题涉及解释,特别是解释所涉及的原则是如何应用的。@EJP:此外,即使该问题在其他地方得到了回答,作为完全重复的问题结束问题的标准不是答案是否存在于其他地方,也不是在某个其他问题答案的某个链接的末尾,但它是否是一个精确的复制品。这个问题不是完全重复的。谢谢Eric-你能解释一下错误吗?这是什么?为什么我们会得到这个错误值?是因为我们不能得到1的精确十进制表示吗。1@SanthoshReddyMandadi:我在答案中添加了细节。
public static void main(String args[]) {
    double a = 0.9d;
    System.out.println(a);
    System.out.println(new BigDecimal(a));
    double b = 2d - 1.1d;
    System.out.println(b);
    System.out.println(new BigDecimal(2.0d));
    System.out.println(new BigDecimal(1.1d));
    System.out.println(new BigDecimal(b));
}
0.9
0.90000000000000002220446049250313080847263336181640625
0.8999999999999999
2
1.100000000000000088817841970012523233890533447265625
0.899999999999999911182158029987476766109466552734375