Problem with my math?? (pow)

Hi,

I just got my first Arduino a week or so ago and I working on my first sketch where I am using an encoder to control a bank of relays via a shift register. The majority of what I have so far is to do with reading the encoder which was taken from a tutorial. Once I have the up/down count from the encoder I basically want it to increment which bit it is affecting, so that when the encoder is turned it cycles through the following states:

00000001
00000010
00000100
00001000
00010000
00100000
01000000
10000000

So the first thing I have done is use a modulo to loop the encoder count between 0 and 7. I figure that the binary values above equate to 2^0 through to 2^7. So i have then used the pow function with a base of 2 and the modulo'd encoder count as the exponent. As far as I can tell this should work ok and give me values of 1,2,4,8,16,32,64 and 128. For 2^0 and 2^1 this works fine, but for some reason when the pow function is doing a anything between 2^2 and 2^7 the numerical value comes out as 1 below what it should, i.e 3 instead of 4, 7 instead of 8, 15 instead of 16 etc.

If i substitute my modulo'd count for set integer then it works fine :s

I have set up the serial print so that the 1st column shows the modulo'd encoder read out, the 2nd column is the 2^ output, and then the 3rd column is the binary form.

Can anyone help with this?

Thanks

 int encoder0PinA = 3;
 int encoder0PinB = 2;
 unsigned int encoder0Pos = -1;
 int encoder0PinALast = LOW;
 int n = LOW;
 int channelSelect;
 int relaySelect;

 void setup() { 
   
   pinMode (encoder0PinA,INPUT);
   pinMode (encoder0PinB,INPUT); 
   Serial.begin (9600);
} 

 void loop() { 
   n = digitalRead(encoder0PinA);
   if ((encoder0PinALast == LOW) && (n == HIGH)) {
     if (digitalRead(encoder0PinB) == LOW) {
       encoder0Pos--;
     } else {
       encoder0Pos++;
     }
     channelSelect = (encoder0Pos % 8); // Modulo loop to 0-7
     Serial.print (channelSelect); 
     Serial.print ("  /  ");
     relaySelect = pow(2, channelSelect);
     Serial.print (relaySelect);
     Serial.print ("  /  ");
     Serial.println (relaySelect, BIN);
   } 
   encoder0PinALast = n;
 }

Fascinating! I sure can't figure it out, but here's corroboration...

void setup() {
unsigned int bitbasket;
  Serial.begin(115200);
  for (unsigned int i = 0; i<100; i++) {
    bitbasket = pow( 2, i % 8);
    Serial.print(bitbasket);
    Serial.print("      ");
    Serial.println(pow( 2, i % 8));
  }
}

void loop() {
 }

pow() is a floating point function and as you have discovered doesn't do well with integer arguments unless you're careful.
Use bit shifting instead.

Pete

You will probably like this especially if you're playing with leds.

halfway down on the left, Bitwise Operators

This is the page you need now, the others are for when you say cool, what's next?

Note that right shift can be different with a signed variable or unsigned variable.

What am I missing here? If you do a bitwise shift left, you're also doing powers of 2, and that's a hell of a lot faster than pow().

Missing that pow() only works on floats and might not return exact results?

OK thanks guys, I will have a go with BitShift, it looks fairly simple :slight_smile: So is there any particular reason why pow() is failing or is it just one of those things you have to know and remember?

Cheers

It isn't exactly failing, it's just a feature of floating point accuracy. pow(2,4) Is probably calculating as 15.999 or similar, but when you try and put that into an int it will truncate to 15, giving you your off by one issue. You could probably fix it for your purposes by adding 0.01. No reason to do so though because pow isn't a very efficient way of doing what you need - bitshift is, as you see, better.

The other bit of confusion is why it worked when you put literal numbers in instead of a variable. That was the compiler noticing that what you were calculating was in fact a constant and optimizing the call away. The compiler apparently did a better job of managing the calculation.

miraphonic:
OK thanks guys, I will have a go with BitShift, it looks fairly simple :slight_smile: So is there any particular reason why pow() is failing or is it just one of those things you have to know and remember?

Cheers

Arduino floating point is for when slowww and close is good enough.
Integers and some other methods are for when you have to be exact or just don't want to wait.

If you ever write payroll or billing apps, the smart way is to use integers and even then take care about divides.
I've written both long ago. When my electric billing package was checked against PECO's and some bills were 1 or 2 cents different, Herb Dennenberg's team found that MY code was right and PECO's was off! But to be fair it only happened when pro-rating was involved, two or so times a year per customer.