1

I have an Arduino Nano and was adding up some values 2^i from 0..7:

void setup() {
  Serial.begin(9600);
  int i,k;
  k = 0;
  for (i=0;i<8;i++){
     k += pow(2, i);
     Serial.println(k);
  }
}

Those numbers should sum up to 255, but that's the output:

1
3
6
13
28
59
122
249

Is my Arduino not working or what can be the reason for this behaviour?

1
  • As a tip for the future, printing the intermediate results would have given you a clue. For example, adding Serial.print(pow(2, i), 7); gave me: 1.0000000, 2.0000000, 3.9999995, 7.9999980, 15.9999961, 31.9999885, 63.9999771, 127.9999542. The default output of Serial.print rounds so doing Serial.print(pow(2, i)); masks the problem: 1.00, 2.00, 4.00, 8.00, 16.00, 32.00, 64.00, 128.00,
    – Nick Gammon
    Aug 14, 2016 at 21:12

2 Answers 2

3

No, there is nothing "wrong" there. In fact it's the expected results.

The problem here is that pow() returns a float, and some numbers just can't be represented with floating point. When that floating point value is converted to an integer it truncates the decimal portion leaving you with the wrong integer value.

void setup() {
    Serial.begin(115200);

    int t = 0;
    for (int i = 0; i < 8; i++) {
        float f = pow(2, i);
        int d = f;
        t += f;
        Serial.print("2^");
        Serial.print(i);
        Serial.print(": float = ");
        Serial.print(f, 10);
        Serial.print(" int = ");
        Serial.print(d);
        Serial.print(" total = ");
        Serial.println(t);
    }
}

void loop() {
}

2^0: float = 1.0000000000 int = 1 total = 1
2^1: float = 2.0000000000 int = 2 total = 3
2^2: float = 3.9999995231 int = 3 total = 6
2^3: float = 7.9999980926 int = 7 total = 13
2^4: float = 15.9999961853 int = 15 total = 28
2^5: float = 31.9999885559 int = 31 total = 59
2^6: float = 63.9999771118 int = 63 total = 122
2^7: float = 127.9999542236 int = 127 total = 249

Instead you should use bit-shifting to perform the same operation on an integer:

void setup() {
    Serial.begin(115200);

    int t = 0;
    for (int i = 0; i < 8; i++) {
        int d = 1 << i;
        t += d;
        Serial.print("1<<");
        Serial.print(i);
        Serial.print(": int = ");
        Serial.print(d);
        Serial.print(" total = ");
        Serial.println(t);
    }
}

void loop() {
}

1<<0: int = 1 total = 1
1<<1: int = 2 total = 3
1<<2: int = 4 total = 7
1<<3: int = 8 total = 15
1<<4: int = 16 total = 31
1<<5: int = 32 total = 63
1<<6: int = 64 total = 127
1<<7: int = 128 total = 255
2
  • 3
    Actually the values in question most definitely can be precisely represented as floats - all small integers can be, and these are even powers of two on a binary system. The issue is rather imprecision within the implementation of the pow() function, possibly having to do with intermediate values. And yes, it is not the way to do this - even apart from the inaccurate results, it is far more computationally intensive. Aug 14, 2016 at 18:07
  • @ChrisStratton Float is still crap either way. Q15 etc are the way forward.
    – Majenko
    Aug 14, 2016 at 18:09
0

Granted that integer arithmetic is a computationally better approach to this problem; if your intent was to learn to use floating point and float to int conversions, then you need to round up the floating result before the conversion truncates it. As you saw in @Majenko's example, the floating results are close but not quite exact. In your own example, you'd change

k += pow(2, i);

to

p = pow(2, i); k += (p < 0. ? -0.5 : 0.5);

to so the float would truncate to the nearest int instead to the next lower int.

2
  • Note: that trick only works for positive numbers. For negative ones you should subtract 0.5. Or use the round() function.
    – Majenko
    Aug 14, 2016 at 19:07
  • Yup, shot from the hip. Edited to show the non-round() version.
    – JRobert
    Aug 14, 2016 at 22:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.