Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/email/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么更改求和顺序会返回不同的结果?_Java_Javascript_Floating Point - Fatal编程技术网

Java 为什么更改求和顺序会返回不同的结果?

Java 为什么更改求和顺序会返回不同的结果?,java,javascript,floating-point,Java,Javascript,Floating Point,为什么更改求和顺序会返回不同的结果 23.53+5.88+17.64=47.05 23.53+17.64+5.88=47.05000000000004 两者都返回相同的结果 我知道,由于浮点数在二进制中的表示方式,一些有理数(如1/3-0.333333…)无法精确表示 为什么仅仅改变元素的顺序就会影响结果?我认为这与评估的顺序有关。虽然在数学世界中,和自然是一样的,但在二进制世界中,不是a+B+C=D,而是 A + B = E E + C = D(1) 这是第二步,浮点数可以从中得到 当你改变

为什么更改求和顺序会返回不同的结果

23.53+5.88+17.64
=
47.05

23.53+17.64+5.88
=
47.05000000000004

两者都返回相同的结果

我知道,由于浮点数在二进制中的表示方式,一些有理数(如1/3-0.333333…)无法精确表示


为什么仅仅改变元素的顺序就会影响结果?

我认为这与评估的顺序有关。虽然在数学世界中,和自然是一样的,但在二进制世界中,不是a+B+C=D,而是

A + B = E
E + C = D(1)
这是第二步,浮点数可以从中得到

当你改变顺序时

A + C = F
F + B = D(2)
也许这个问题很愚蠢,但是为什么仅仅改变元素的顺序就会影响结果呢

它将根据值的大小更改值的舍入点。作为我们看到的这类事情的一个例子,让我们假设我们使用的不是二进制浮点,而是具有4个有效数字的十进制浮点类型,其中每个加法以“无限”精度执行,然后四舍五入到最接近的可表示数。这里有两个总数:

1/3 + 2/3 + 2/3 = (0.3333 + 0.6667) + 0.6667
                = 1.000 + 0.6667 (no rounding needed!)
                = 1.667 (where 1.6667 is rounded to 1.667)

2/3 + 2/3 + 1/3 = (0.6667 + 0.6667) + 0.3333
                = 1.333 + 0.3333 (where 1.3334 is rounded to 1.333)
                = 1.666 (where 1.6663 is rounded to 1.666)
我们甚至不需要非整数来解决这个问题:

10000 + 1 - 10000 = (10000 + 1) - 10000
                  = 10000 - 10000 (where 10001 is rounded to 10000)
                  = 0

10000 - 10000 + 1 = (10000 - 10000) + 1
                  = 0 + 1
                  = 1
这可能更清楚地表明,重要的部分是有效数字的数量有限,而不是小数位数的数量有限。如果我们可以始终保持相同的小数位数,那么至少使用加法和减法,就可以了(只要值没有溢出)。问题是,当你得到较大的数字时,较小的信息就会丢失——在本例中,10001被四舍五入到10000。(这是问题的一个例子。)


需要注意的是,右侧第一行的值在所有情况下都是相同的-因此,尽管需要了解十进制数(23.53、5.88、17.64)不会精确表示为
双精度值,但这一点很重要,这仅仅是由于上面所示的问题造成的问题。

浮点数使用IEEE 754格式表示,该格式为尾数(有效位)提供特定大小的位。不幸的是,这为您提供了特定数量的“分数构建块”,并且某些分数值无法精确表示

在第二种情况下,由于计算加法的顺序,加法可能会遇到一些精度问题。我还没有计算出这些值,但可能是23.53+17.64无法精确表示,而23.53+5.88可以


不幸的是,这是一个您必须处理的已知问题。

这实际上涉及的不仅仅是Java和Javascript,而且可能会影响任何使用浮点或双精度的编程语言

在内存中,浮点使用了一种特殊的格式,符合IEEE 754(转换器提供了比我更好的解释)

无论如何,这是浮子转换器

关于操作顺序的问题是操作的“精细度”

你的第一行从前两个值得到29.41,这给了我们2^4作为指数

你的第二行得到41.17,这给我们2^5作为指数

通过增加指数,我们正在失去一个重要的数字,这可能会改变结果

尝试在最右边的最后一位上勾选41.17,您可以看到,指数的1/2^23这样“无关紧要”的东西足以导致这个浮点差


编辑:对于那些记得重要数字的人来说,这属于那个类别。有效数字为1的10^4+4999将是10^4。在本例中,有效数字要小得多,但我们可以看到附加了.0000000000 4的结果。

以下是二进制代码。我们知道,有些浮点值不能用二进制精确表示,即使它们可以用十进制精确表示。这3个数字只是这一事实的例子

使用这个程序,我输出每个数字的十六进制表示形式和每个加法的结果

public class Main{
   public static void main(String args[]) {
      double x = 23.53;   // Inexact representation
      double y = 5.88;    // Inexact representation
      double z = 17.64;   // Inexact representation
      double s = 47.05;   // What math tells us the sum should be; still inexact

      printValueAndInHex(x);
      printValueAndInHex(y);
      printValueAndInHex(z);
      printValueAndInHex(s);

      System.out.println("--------");

      double t1 = x + y;
      printValueAndInHex(t1);
      t1 = t1 + z;
      printValueAndInHex(t1);

      System.out.println("--------");

      double t2 = x + z;
      printValueAndInHex(t2);
      t2 = t2 + y;
      printValueAndInHex(t2);
   }

   private static void printValueAndInHex(double d)
   {
      System.out.println(Long.toHexString(Double.doubleToLongBits(d)) + ": " + d);
   }
}
printValueAndInHex
方法只是一个十六进制打印机助手

结果如下:

403787ae147ae148: 23.53
4017851eb851eb85: 5.88
4031a3d70a3d70a4: 17.64
4047866666666666: 47.05
--------
403d68f5c28f5c29: 29.41
4047866666666666: 47.05
--------
404495c28f5c28f6: 41.17
4047866666666667: 47.050000000000004
前4个数字是
x
y
z
s
的十六进制表示形式。在IEEE浮点表示法中,位2-12表示二进制指数,即数字的刻度。(第一位是符号位,剩余的位是尾数。)表示的指数实际上是二进制数减去1023

提取前4个数字的指数:

    sign|exponent
403 => 0|100 0000 0011| => 1027 - 1023 = 4
401 => 0|100 0000 0001| => 1025 - 1023 = 2
403 => 0|100 0000 0011| => 1027 - 1023 = 4
404 => 0|100 0000 0100| => 1028 - 1023 = 5
第一组新增内容

第二个数字(
y
)的大小较小。当将这两个数字相加以获得
x+y
时,第二个数字(
01
)的最后2位将移位到范围之外,并且不计入计算中

第二个加法加上
x+y
z
,并加上两个相同刻度的数字

第二组添加项

在这里,
x+z
首先出现。它们的规模相同,但它们产生的数字规模更大:

404 => 0|100 0000 0100| => 1028 - 1023 = 5
第二次加法添加
x+z
y
,现在从
y
中删除3位以添加数字(
101
)。这里必须向上舍入,因为结果是下一个浮点数向上:
4047866666
用于第一组加法,而
4047866667
用于第二组加法。那个e
x1 = (a - b) + (c - d) + (e - f) + (g - h);
x2 = (a + c + e + g) - (b + d + f + h);
x3 = a - b + c - d + e - f + g - h;