Arduino - 具有pow(x,y)函数的奇数指数行为

时间:2013-11-27 03:40:44

标签: c++ arduino

我花了几个小时调试这个问题,最终自己解决了。以为我会在这里发布,以防止其他人遇到同样荒谬的问题。

对于为什么我的回答是一个解释,我肯定愿意接受更深入的解释。


我一直在使用pow(x,y)函数来返回指数。我注意到指数的非常奇怪的行为,我不明白为什么。这是我的代码:

for (int n=0;n<5;n++)
{
    int x = pow(2,n);
    Serial.print(n); 
    Serial.print(" ");
    Serial.println(x);
}

这是我的输出:

0 1
1 2
2 3
3 7
4 15

所以这些数字显然不对。奇怪的是,当我在Xcode中的C ++程序中运行相同的代码(使用cout语句而不是串行输出)时,我得到以下内容(我期望):

0 1
1 2
2 4
3 8
4 16

为什么世界会在Xcode中返回我的预期值而不是在arduino上?为什么arduino会为{em> n 大于1的值返回pow(2,n) = 2^n-1

4 个答案:

答案 0 :(得分:4)

由于AVR没有FPU,avr-libc中的pow()是通过调用log()exp()实现的。同样,由于缺少FPU,avr-libc对这两个函数使用近似。这会导致值与真值稍微偏离,当转换为整数时,可能会丢失最低有效数字。

这在x86级系统上不会发生,因为它们具有硬件FPU,能够为正整数的非负幂提供真正的积分值。

我的建议是,如果您需要的只是整数的非负整数幂,那么您应该执行一系列按位移位并添加而不是必须链接到非平凡的非精确libm

答案 1 :(得分:3)

啊,你这个少年傻瓜,瑞恩!你不了解数据类型!?

The Arduino pow() reference明确指出这些值必须作为浮点数传递并作为双精度返回!所以,让我们使用一些脑细胞,至少尝试返回双倍!

这里有一些代码来突出正在发生的疯狂:

for (int n=0;n<5;n++)
{
    double x = pow(2,n);
    Serial.print(n); 
    Serial.print(" ");
    Serial.print(x);
    Serial.print(" ");
    Serial.println((int)x); // cast as int here
}

这是你的输出:

0 1.00 1
1 2.00 2
2 4.00 3
3 8.00 7
4 16.00 15

无论如何,这将解决您的问题。将数字转换为int表示它向下舍入。


现在,为什么会这样?不太确定。

答案 2 :(得分:0)

正如Ignacio Vazquez-Abrams的答案(这是 正确答案),我编写了以下程序来测试其准确性 pow(2, i)的正整数值为i

#include <stdio.h>
#include <math.h>
#include <float.h>
#include <inttypes.h>

int main(void)
{
    printf("  i          2^i     correct   pow(2, i)         error  ulps\n");
    printf("------------------------------------------------------------\n");
    union { float f; uint32_t i; } x, y;
    x.f = 1;                    // 2^i, correct value
    float ulp_r = FLT_EPSILON;  // ULP to the right of x
    for (int i = 0; i < 128; i++, x.f *= 2, ulp_r *= 2) {
        y.f = pow(2, i);
        float error = y.f - x.f;
        float ulp = error < 0 ? ulp_r/2 : ulp_r;  // ULP(x - error)
        printf("%3d  %11.6g  0x%08"PRIx32"  0x%08"PRIx32"  %12.6g  %4g\n",
                i, x.f, x.i, y.i, error, error/ulp);
    }
    return 0;
}

在我的电脑上(gcc 5.4.0 / Ubuntu 16.04),该程序报告零错误。 在Arduino Uno上运行相同的程序(使用适当的stdio设置) (avr-gcc 4.9.2 / avr-libc 1.8.0),我得到的错误一样大 90 ULPs!以下是Uno的输出:

  i          2^i     correct   pow(2, i)         error  ulps
------------------------------------------------------------
  0            1  0x3f800000  0x3f800000             0     0
  1            2  0x40000000  0x40000000             0     0
  2            4  0x40800000  0x407ffffe  -4.76837e-07    -2
  3            8  0x41000000  0x40fffffc  -1.90735e-06    -4
  4           16  0x41800000  0x417ffffc   -3.8147e-06    -4
  5           32  0x42000000  0x41fffffa  -1.14441e-05    -6
  6           64  0x42800000  0x427ffffa  -2.28882e-05    -6
  7          128  0x43000000  0x42fffffa  -4.57764e-05    -6
  8          256  0x43800000  0x437ffffa  -9.15527e-05    -6
  9          512  0x44000000  0x43fffff4  -0.000366211   -12
 10         1024  0x44800000  0x447ffff4  -0.000732422   -12
 11         2048  0x45000000  0x44fffff4   -0.00146484   -12
 12         4096  0x45800000  0x457ffff4   -0.00292969   -12
 13         8192  0x46000000  0x46000000             0     0
 14        16384  0x46800000  0x467ffff4    -0.0117188   -12
 15        32768  0x47000000  0x46fffff4    -0.0234375   -12
 16        65536  0x47800000  0x477ffff4     -0.046875   -12
 17       131072  0x48000000  0x48000000             0     0
 18       262144  0x48800000  0x487fffea      -0.34375   -22
 19       524288  0x49000000  0x48ffffea       -0.6875   -22
 20  1.04858e+06  0x49800000  0x497fffea        -1.375   -22
 21  2.09715e+06  0x4a000000  0x4a000000             0     0
 22   4.1943e+06  0x4a800000  0x4a7fffea          -5.5   -22
 23  8.38861e+06  0x4b000000  0x4affffea           -11   -22
 24  1.67772e+07  0x4b800000  0x4b7fffea           -22   -22
 25  3.35544e+07  0x4c000000  0x4c000000             0     0
 26  6.71089e+07  0x4c800000  0x4c800000             0     0
 27  1.34218e+08  0x4d000000  0x4cffffea          -176   -22
 28  2.68435e+08  0x4d800000  0x4d7fffea          -352   -22
 29  5.36871e+08  0x4e000000  0x4e000000             0     0
 30  1.07374e+09  0x4e800000  0x4e7fffea         -1408   -22
 31  2.14748e+09  0x4f000000  0x4effffea         -2816   -22
 32  4.29497e+09  0x4f800000  0x4f7fffea         -5632   -22
 33  8.58993e+09  0x50000000  0x50000000             0     0
 34  1.71799e+10  0x50800000  0x50800000             0     0
 35  3.43597e+10  0x51000000  0x50ffffd2        -94208   -46
 36  6.87195e+10  0x51800000  0x517fffd2       -188416   -46
 37  1.37439e+11  0x52000000  0x52000000             0     0
 38  2.74878e+11  0x52800000  0x527fffd2       -753664   -46
 39  5.49756e+11  0x53000000  0x52ffffd2  -1.50733e+06   -46
 40  1.09951e+12  0x53800000  0x537fffd2  -3.01466e+06   -46
 41  2.19902e+12  0x54000000  0x54000000             0     0
 42  4.39805e+12  0x54800000  0x54800000             0     0
 43  8.79609e+12  0x55000000  0x54ffffd2  -2.41172e+07   -46
 44  1.75922e+13  0x55800000  0x557fffd2  -4.82345e+07   -46
 45  3.51844e+13  0x56000000  0x56000000             0     0
 46  7.03687e+13  0x56800000  0x567fffd2  -1.92938e+08   -46
 47  1.40737e+14  0x57000000  0x57000000             0     0
 48  2.81475e+14  0x57800000  0x577fffd2  -7.71752e+08   -46
 49   5.6295e+14  0x58000000  0x57ffffd2   -1.5435e+09   -46
 50   1.1259e+15  0x58800000  0x58800000             0     0
 51   2.2518e+15  0x59000000  0x58ffffd2  -6.17402e+09   -46
 52   4.5036e+15  0x59800000  0x59800000             0     0
 53   9.0072e+15  0x5a000000  0x5a000000             0     0
 54  1.80144e+16  0x5a800000  0x5a7fffd2  -4.93921e+10   -46
 55  3.60288e+16  0x5b000000  0x5b000000             0     0
 56  7.20576e+16  0x5b800000  0x5b7fffd2  -1.97568e+11   -46
 57  1.44115e+17  0x5c000000  0x5bffffd2  -3.95137e+11   -46
 58   2.8823e+17  0x5c800000  0x5c800000             0     0
 59  5.76461e+17  0x5d000000  0x5cffffd2  -1.58055e+12   -46
 60  1.15292e+18  0x5d800000  0x5d7fffd2   -3.1611e+12   -46
 61  2.30584e+18  0x5e000000  0x5e000000             0     0
 62  4.61169e+18  0x5e800000  0x5e7fffd2  -1.26444e+13   -46
 63  9.22337e+18  0x5f000000  0x5f000000             0     0
 64  1.84467e+19  0x5f800000  0x5f7fffd2  -5.05775e+13   -46
 65  3.68935e+19  0x60000000  0x5fffffa6  -1.97912e+14   -90
 66   7.3787e+19  0x60800000  0x60800000             0     0
 67  1.47574e+20  0x61000000  0x60ffffa6  -7.91648e+14   -90
 68  2.95148e+20  0x61800000  0x61800000             0     0
 69  5.90296e+20  0x62000000  0x61ffffa6  -3.16659e+15   -90
 70  1.18059e+21  0x62800000  0x627fffa6  -6.33319e+15   -90
 71  2.36118e+21  0x63000000  0x63000000             0     0
 72  4.72237e+21  0x63800000  0x637fffa6  -2.53327e+16   -90
 73  9.44473e+21  0x64000000  0x63ffffa6  -5.06655e+16   -90
 74  1.88895e+22  0x64800000  0x64800000             0     0
 75  3.77789e+22  0x65000000  0x64ffffa6  -2.02662e+17   -90
 76  7.55579e+22  0x65800000  0x657fffa6  -4.05324e+17   -90
 77  1.51116e+23  0x66000000  0x65ffffa6  -8.10648e+17   -90
 78  3.02231e+23  0x66800000  0x667fffa6   -1.6213e+18   -90
 79  6.04463e+23  0x67000000  0x67000000             0     0
 80  1.20893e+24  0x67800000  0x677fffa6  -6.48518e+18   -90
 81  2.41785e+24  0x68000000  0x67ffffa6  -1.29704e+19   -90
 82   4.8357e+24  0x68800000  0x68800000             0     0
 83  9.67141e+24  0x69000000  0x68ffffa6  -5.18815e+19   -90
 84  1.93428e+25  0x69800000  0x69800000             0     0
 85  3.86856e+25  0x6a000000  0x69ffffa6  -2.07526e+20   -90
 86  7.73713e+25  0x6a800000  0x6a7fffa6  -4.15052e+20   -90
 87  1.54743e+26  0x6b000000  0x6b000000             0     0
 88  3.09485e+26  0x6b800000  0x6b7fffa6  -1.66021e+21   -90
 89   6.1897e+26  0x6c000000  0x6bffffa6  -3.32041e+21   -90
 90  1.23794e+27  0x6c800000  0x6c800000             0     0
 91  2.47588e+27  0x6d000000  0x6cffffa6  -1.32817e+22   -90
 92  4.95176e+27  0x6d800000  0x6d7fffa6  -2.65633e+22   -90
 93  9.90352e+27  0x6e000000  0x6dffffa6  -5.31266e+22   -90
 94   1.9807e+28  0x6e800000  0x6e800000             0     0
 95  3.96141e+28  0x6f000000  0x6f000000             0     0
 96  7.92282e+28  0x6f800000  0x6f7fffa6  -4.25013e+23   -90
 97  1.58456e+29  0x70000000  0x6fffffa6  -8.50026e+23   -90
 98  3.16913e+29  0x70800000  0x707fffa6  -1.70005e+24   -90
 99  6.33825e+29  0x71000000  0x71000000             0     0
100  1.26765e+30  0x71800000  0x71800000             0     0
101   2.5353e+30  0x72000000  0x71ffffa6  -1.36004e+25   -90
102   5.0706e+30  0x72800000  0x727fffa6  -2.72008e+25   -90
103  1.01412e+31  0x73000000  0x72ffffa6  -5.44017e+25   -90
104  2.02824e+31  0x73800000  0x73800000             0     0
105  4.05648e+31  0x74000000  0x74000000             0     0
106  8.11296e+31  0x74800000  0x74800000             0     0
107  1.62259e+32  0x75000000  0x74ffffa6  -8.70427e+26   -90
108  3.24519e+32  0x75800000  0x757fffa6  -1.74085e+27   -90
109  6.49037e+32  0x76000000  0x75ffffa6  -3.48171e+27   -90
110  1.29807e+33  0x76800000  0x76800000             0     0
111  2.59615e+33  0x77000000  0x77000000             0     0
112   5.1923e+33  0x77800000  0x777fffa6  -2.78537e+28   -90
113  1.03846e+34  0x78000000  0x77ffffa6  -5.57073e+28   -90
114  2.07692e+34  0x78800000  0x787fffa6  -1.11415e+29   -90
115  4.15384e+34  0x79000000  0x79000000             0     0
116  8.30767e+34  0x79800000  0x79800000             0     0
117  1.66153e+35  0x7a000000  0x79ffffa6  -8.91317e+29   -90
118  3.32307e+35  0x7a800000  0x7a7fffa6  -1.78263e+30   -90
119  6.64614e+35  0x7b000000  0x7affffa6  -3.56527e+30   -90
120  1.32923e+36  0x7b800000  0x7b7fffa6  -7.13053e+30   -90
121  2.65846e+36  0x7c000000  0x7c000000             0     0
122  5.31691e+36  0x7c800000  0x7c800000             0     0
123  1.06338e+37  0x7d000000  0x7cffffa6  -5.70443e+31   -90
124  2.12676e+37  0x7d800000  0x7d7fffa6  -1.14089e+32   -90
125  4.25353e+37  0x7e000000  0x7dffffa6  -2.28177e+32   -90
126  8.50706e+37  0x7e800000  0x7e800000             0     0
127  1.70141e+38  0x7f000000  0x7f000000             0     0

值得注意的几点:

  • FLT_MINFLT_MAX之间的每两个幂都是正确的 可表示为float
  • IEEE-754标准要求操作+, - ,×,÷和√ 正确舍入,这意味着计算2 i 迭代地乘以2可以保证得到精确的结果
  • 相同的标准要求pow正确舍入。

答案 3 :(得分:-1)

老问题,但对于未来的问题:

将16.0向下舍入为15的原因是,从float到int的转换始终是截断小数。所以15.99999仍然是15而不是16。

由于浮点值不能精确地包含16.0,因此它们具有类似于15.99963513(随机示例)的内容,在转换为整数时变为15。