【问题标题】:About floating point precision: why the iteration numbers are not equal?关于浮点精度:为什么迭代次数不相等?
【发布时间】:2011-06-25 10:23:37
【问题描述】:

有两个类似的matlab程序,一个迭代10次,另一个迭代11次。

一个:

i = 0;
x = 0.0;
h = 0.1;
while x < 1.0
    i = i + 1;
    x = i * h;
    disp([i,x]);
end

另一个:

i = 0;
x = 0.0;
h = 0.1;
while x < 1.0
    i = i + 1;
    x = x + h;
    disp([i,x]);
end

我不明白为什么浮点加法和倍数有区别。

【问题讨论】:

  • 这可能是由于变量 x 中的 fp 表示。因此,在最后一种情况下,x 可能略小于 1,因为总和的误差传播更大。实际输出是多少?您看到的最后一个 x 值是哪个?
  • @Howard 前者的x值为1,后者的x值为1.1。
  • @Cook 尝试输出 x-1 而不是 x 本身。然后您应该会看到不同之处(请参阅下面我的答案中的示例)。
  • 我打算链接到 (Goldberg 1991),然后发现:floating-point-gui.de

标签: matlab floating-point


【解决方案1】:

当您使用浮点计数器进行迭代时,您应该非常小心。作为一个例子,我会告诉你在你的情况下会发生什么(它是一个 Java 程序,但你的情况应该是一样的):click here to run it yourself

double h = 0.1;
System.out.println(10*h-1.0);
System.out.println(h+h+h+h+h+h+h+h+h+h-1.0);

在进行乘法与单独的加法时,它只会将差值打印为 1。

由于浮点数的表示不准确,结果如下所示:

0.0
-1.1102230246251565E-16

因此,如果您在后一种情况下将其用作循环条件,则会有一个额外的迭代(尚未达到)。

尝试使用计数器变量i,它是一个整数,你不会遇到这样的问题。

【讨论】:

  • 谢谢。 matlab 程序是从《初等数值分析》(K. Atkinson) 一书中的练习中复制而来的。我想知道为什么会有差异。
【解决方案2】:

比较以下输出:

>> fprintf('%0.20f\n', 0.1.*(1:10))
0.10000000000000001000
0.20000000000000001000
0.30000000000000004000
0.40000000000000002000
0.50000000000000000000
0.60000000000000009000
0.70000000000000007000
0.80000000000000004000
0.90000000000000002000
1.00000000000000000000

>> fprintf('%0.20f\n', cumsum(repmat(0.1,1,10)))
0.10000000000000001000
0.20000000000000001000
0.30000000000000004000
0.40000000000000002000
0.50000000000000000000
0.59999999999999998000
0.69999999999999996000
0.79999999999999993000
0.89999999999999991000
0.99999999999999989000

还可以与使用 MATLAB 的 COLON 运算符进行比较:

>> fprintf('%0.20f\n', 0.1:0.1:1)
0.10000000000000001000
0.20000000000000001000
0.30000000000000004000
0.40000000000000002000
0.50000000000000000000
0.59999999999999998000
0.69999999999999996000
0.80000000000000004000
0.90000000000000002000
1.00000000000000000000

如果要查看 64 位二进制表示,请使用:

>> format hex
>> [(0.1:0.1:1)' (0.1.*(1:10))' cumsum(repmat(0.1,10,1))]
   3fb999999999999a   3fb999999999999a   3fb999999999999a
   3fc999999999999a   3fc999999999999a   3fc999999999999a
   3fd3333333333334   3fd3333333333334   3fd3333333333334
   3fd999999999999a   3fd999999999999a   3fd999999999999a
   3fe0000000000000   3fe0000000000000   3fe0000000000000
   3fe3333333333333   3fe3333333333334   3fe3333333333333
   3fe6666666666666   3fe6666666666667   3fe6666666666666
   3fe999999999999a   3fe999999999999a   3fe9999999999999
   3feccccccccccccd   3feccccccccccccd   3feccccccccccccc
   3ff0000000000000   3ff0000000000000   3fefffffffffffff

一些推荐读物(MATLAB 相关):

【讨论】:

  • 谢谢。事实上,我已经测试了你测试的内容。我知道这些现象,但我无法解释为什么累积和乘法之间存在差异。
  • 重复添加会累积舍入错误。由于浮点数的精度有限,因此将结果四舍五入以适合有限数量的位。我强烈建议您查看我提供的链接。
  • 谢谢。我现在明白了。
【解决方案3】:

浮点数的表示是精确的,除了浮点运算是以 2 为基数的,而小数(例如 0.1)具有无限的二进制扩展。由于浮点数的位数是有限的,所以 0.1 的无限扩展必须四舍五入,并且舍入误差在相加时会累积,从而导致差异。

但是,大多数浮点操作是不精确的:结果通常需要比固定位数更多的精度,因此 CPU 会自动将结果四舍五入以适应可用的精确。正如您所注意到的,这种舍入误差会在长计算链中累积,并且有时会导致实际结果和“正确”结果之间存在巨大差异。 (“正确”被定义为在无限精度算术中获得的结果。)

【讨论】:

  • 浮点表示不准确。它对于特定的一组数字是精确的,但对于任何可能的数字都不是。您自己说过:二进制扩展中的 0.1(或 1/10)有无数位必须被截断,因此不存在二进制浮点数中 1/10 的精确表示!您注意到十进制表示法中的相同点:1/3 (0.3333...) 不能用十进制扩展中的有限位数精确表示。
  • 没错,浮点数不能代表所有可以想象的实数,其中有 0.1,但可表示数字的集合是完全可表示的。例如,0.125=1/8 是精确表示的,表示没有不精确。 0.1 不是完全可表示的数字,但 sqrt(2) 也不是,当仅限于基于有限基数的表示时。那你的意思是什么?
  • 我的意思是,您所说的“浮点表示是精确的”是不正确的。它适用于很多数字,但并非适用于所有数字(十进制扩展是有限的,并且限制为大约 15 或 16 位小数(在双精度的情况下))。所以表示是不准确的(一般来说),因为没有从十进制扩展(这是写入文字的常用方法)到导致错误 0 的二进制扩展的映射。说表示对于可表示的数字是准确的是就像说所有红色的车都是红色的,这是真的,但没有什么价值。
  • 所以您对准确性的参考是十进制数?那么,你如何用有限的十进制数字精确地表示 1/7? (实际上,从十进制浮点数到二进制浮点数的 映射 并不精确,但这并不会使浮点数不精确。)
  • 我在哪里说十进制浮点数是精确的?他们不是。文字最常见的表示是十进制扩展(即你写 x=0.1 或类似的东西),所以你希望二进制表示完全匹配,但它不能。 1/7 的表示是精确的,但没有十进制(或二进制)精确的浮点表示。所以浮动通常不准确。我对精确性的引用是程序员用来定义文字的表示(并且在任何基数中都没有可以准确表示这些的浮点数)。
猜你喜欢
  • 1970-01-01
  • 2021-04-17
  • 1970-01-01
  • 1970-01-01
  • 2013-03-05
  • 2016-02-23
  • 2015-12-25
  • 1970-01-01
  • 2020-02-03
相关资源
最近更新 更多