简体   繁体   中英

Arduino - odd exponent behavior with pow(x,y) function

I've spent the past few hours debugging this problem, and eventually solved it myself. Thought I'd post it here to prevent others from experiencing the same ridiculous problem.

I would definitely be open to a deeper explanation as to why my answer is an explanation.


I've been working with the pow(x,y) function to return exponents. I was noticing very weird behavior with the exponents, and I can't quite understand why. Here's my code:

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

And here's my output:

0 1
1 2
2 3
3 7
4 15

So these numbers are obviously not right. Weird thing is, when I run the same code in a C++ program in Xcode (with cout statements instead of Serial output) I get the following (which I would expect):

0 1
1 2
2 4
3 8
4 16

Why in the world would this return my expected values in Xcode but not on the arduino? Why does the arduino return pow(2,n) = 2^n-1 for any value n larger than 1?

Since the AVR doesn't have a FPU, pow() in avr-libc is implemented via calls to log() and exp() . Again, due to the lack of FPU, avr-libc uses approximations for both of those functions. This results in values that will be slightly off from the true value, which when cast to an integer can lose the least significant digit.

This doesn't happen on x86-class systems since those have hardware FPUs which are capable of giving the true integral value for non-negative powers of positive integers.

My suggestion is that if all you need is non-negative integral powers of integers then you should perform a series of bitwise shifts and adds rather than having to link in the non-trivial, non-exact libm .

Ah, you juvenile fool, Ryan! Have you no understanding of data types!?

The Arduino pow() reference explicitly states that these values must be passed as floats and returned as doubles! So let's use some brain cells and at least try returning a double!

Here's some code to highlight the craziness that's going on:

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
}

And here's your output:

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

Anyway, that will solve your problem. Casting the number as an int shows that it gets rounded down.


Now, why does this happen? Not exactly sure.

Just as a complement to Ignacio Vazquez-Abrams' answer (which is the right answer), I wrote the following program to test for the accuracy of pow(2, i) for positive integer values of 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;
}

On my PC (gcc 5.4.0 / Ubuntu 16.04), this program reports zero errors. Running the same program (with proper stdio setup) on an Arduino Uno (avr-gcc 4.9.2 / avr-libc 1.8.0), I get errors as big as 90 ULPs ! Here is the output from the 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

A few points worth noting:

  • every power of two between FLT_MIN and FLT_MAX is exactly representable as a float
  • the IEEE-754 standard requires the operations +, −, ×, ÷ and √ to be correctly rounded, which implies that computing 2 i by iteratively multiplying by 2 is guaranteed to give the exact result
  • the same standard does not require pow to be correctly rounded.

Old question, but for future askers:

The reason 16.0 is rounded down to 15, is that the conversion from float to int is always by truncating the decimals. So 15.99999 still becomes 15 and not 16.

Since floating point values cannot precisely contain 16.0, they hold something like 15.99963513 (random example) which becomes 15 upon casting to an integer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM