(0.3)^ 3 ==(0.3)*(0.3)*(0.3)在matlab中返回false?

时间:2013-01-23 00:29:16

标签: matlab floating-point

我试图理解MATLAB中基本算术运算的舍入误差,我遇到了以下奇怪的例子。

(0.3)^3 == (0.3)*(0.3)*(0.3)

ans = 0

我想确切知道如何计算左侧。 MATLAB documentation表明对于整数幂,使用“通过平方取幂”算法。

“矩阵功率。如果p是标量,则X ^ p是幂p的X.如果p是整数,则通过重复平方来计算功率。”

所以我假设(0.3)^3(0.3)*(0.3)^2会返回相同的值。但这种情况并非如此。如何解释舍入误差的差异?

5 个答案:

答案 0 :(得分:5)

我对MATLAB一无所知,但我在Ruby中尝试过它:

irb> 0.3 ** 3
  => 0.026999999999999996
irb> 0.3 * 0.3 * 0.3
  => 0.027

根据Ruby source code,如果左侧操作数是浮点数,则取幂运算符将右侧操作数转换为浮点数,然后调用标准C函数pow()float函数的pow()变体必须实现更复杂的算法来处理非整数指数,这将使用导致舍入错误的操作。也许MATLAB的工作方式类似。

答案 1 :(得分:4)

有趣的是,标量^似乎是使用pow实现的,而矩阵^是使用square-and-multiply实现的。即:

octave:13> format hex
octave:14> 0.3^3
ans = 3f9ba5e353f7ced8
octave:15> 0.3*0.3*0.3
ans = 3f9ba5e353f7ced9
octave:20> [0.3 0;0 0.3]^3
ans =

  3f9ba5e353f7ced9  0000000000000000
  0000000000000000  3f9ba5e353f7ced9

octave:21> [0.3 0;0 0.3] * [0.3 0;0 0.3] * [0.3 0;0 0.3]
ans =

  3f9ba5e353f7ced9  0000000000000000
  0000000000000000  3f9ba5e353f7ced9

通过在gdb下运行octave并在pow中设置断点来确认。

在matlab中也是如此,但我无法真正验证。

答案 2 :(得分:3)

这是一个小测试程序,它遵循Apple Libm-2026中来自pow()的系统Source/Intel/xmm_power.c在这种情况下的作用:

#include <stdio.h>
int main() {
    // basically lines 1130-1157 of xmm_power.c, modified a bit to remove
    // irrelevant things

    double x = .3;
    int i = 3;

    //calculate ix = f**i
    long double ix = 1.0, lx = (long double) x;

    //calculate x**i by doing lots of multiplication
    int mask = 1;

    //for each of the bits set in i, multiply ix by x**(2**bit_position)
    while(i != 0)
    {
        if( i & mask )
        {
            ix *= lx;
            i -= mask;
        }
        mask += mask;
        lx *= lx; // In double this might overflow spuriously, but not in long double
    }

    printf("%.40f\n", (double) ix);
}

这打印出0.0269999999999999962252417162744677625597,这与我在Matlab中获得.3 ^ 3和在Python中获得.3 ** 3的结果一致(我们知道the latter just calls this code)。相比之下,.3 * .3 * .3对我来说是0.0269999999999999996946886682280819513835,如果你只是要求将0.027打印到那么多的小数位,这就是你得到的同样的事情,因此可能是最接近的两倍。

所以有算法。我们可以准确地找出每个步骤设置的值,但是考虑到不同的算法,它会转到一个非常小的数字并不太令人惊讶。

答案 3 :(得分:3)

感谢@Dougal我发现了这个:

#include <stdio.h>
int main() {
  double x = 0.3;
  printf("%.40f\n", (x*x*x));

  long double y = 0.3;
  printf("%.40f\n", (double)(y*y*y));
}

给出:

0.0269999999999999996946886682280819513835
0.0269999999999999962252417162744677625597

这种情况很奇怪,因为使用更多数字的计算会产生最差的结果。这是因为无论如何,初始数字0.3用几位数近似,因此我们从一个相对“大”的错误开始。在这种特殊情况下,发生的事情是少数位的计算给出另一个“大”错误,但符号相反......因此补偿了初始错误。相反,使用更多数字的计算会产生第二个较小的错误,但第一个仍然存在。

答案 4 :(得分:-7)

阅读Goldberg的"What Every Computer Scientist Should Know About Floating-Point Arithmetic"(这是Oracle的转载)。明白了。浮点数是实数的微积分。对不起,没有TL; DR版本可用。