Reputation: 6644
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?
Upvotes: 2
Views: 8491
Reputation: 3566
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:
FLT_MIN
and FLT_MAX
is exactly
representable as a float
pow
to be correctly rounded.Upvotes: 1
Reputation: 49
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.
Upvotes: -1
Reputation: 798626
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
.
Upvotes: 4
Reputation: 6644
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.
Upvotes: 4