Go Down

Topic: Weird but true pow(x,y) function (Read 3 times) previous topic - next topic

pracas

I'm trying to write a code to convert digital serial input recieved parallely to a decimal value. I'm writing code to convert binary to decimal. Heres a mock up of my code. The weird thing is the pow(x,y) function gives me weird results like int(pow(2,4) ) returns 15 instead of 16 and int(pow(2,0)) returns a proper 1... now whats wrong here? how do i compensate for this error?

Code: [Select]
int j[8];
int i =0, k=0, l=7;

void setup()
{
 j[0]=0; j[1] =0; j[2] =0; j[3] = 1; j[4] =0; j[5] = 1; j[6] = 0; j[7] =1;
 Serial.begin(9600);
}

void loop()
{
l=7;
for(i=0;i<8;i++,l--)
{
k = k + (j[i]*pow(2,l));
Serial.print(k);
}

Serial.println("");
Serial.println(k);
delay(2000);
k=0;
}


:-/
Be The Change...

halley

#1
Feb 13, 2009, 08:06 pm Last Edit: Feb 13, 2009, 08:09 pm by halley Reason: 1
All floating-point math is subject to minor amounts of round-off errors.  When you say pow(2,4) you might get 3.9999999, and when you say (int)(3.99999999) you just make it worse and get 3.

Integer powers of two are ridiculously easy, since all computations are done in binary (a numbering system based on integer powers of two).  If you want 24, then say (1<<4).  It's fast and accurate for integers.

If you're working with mn where both m and n are integers but small, it's still going to be better to do the math with multiplication.  So 43 is best calculated as 4*4*4.

The pow() routine is intended for weird cases like 3.64.8, or with large numbers like 3200062.

Collin80

#2
Feb 13, 2009, 08:22 pm Last Edit: Feb 13, 2009, 08:23 pm by AdderD Reason: 1
I'm guessing that your snippet uses the j[] array to simulate incoming bits on the serial. If that's the case and you want to turn those bits into a digital value then you'd do this:

Code: [Select]

int j[8];
int i =0, k=0;

void setup()
{
 j[0]=0; j[1] =0; j[2] =0; j[3] = 1; j[4] =0; j[5] = 1; j[6] = 0; j[7] =1;
 //Serial.begin(9600);
}

void loop() {
  for(i=0;i<8;i++) {
     k = k + j[i] << (7-i);
 }

 Serial.println("");
 Serial.println(k);
 delay(2000);  
 k=0;
}



That code should be equivilent to yours but using shift operators and without l. You don't need it. If l is going down at the same rate that i is going up then derive it from i.

darudude

#3
Feb 13, 2009, 08:30 pm Last Edit: Feb 13, 2009, 08:30 pm by darudude Reason: 1
If you are only going to use integers (which is what it seems like you are doing), its easy to create a new power function.

Code: [Select]

int powint(int x, int y)
{
 if (y==0)
   return(1);
 else
   return(power(x,y-1)*x);
}

kg4wsv

Be careful with recursive functions on a microcontroller.  In tihs case, if y is very big you'll overrun the stack, trash your RAM, and send your program off into the bushes.

-j


darudude

#5
Feb 13, 2009, 09:09 pm Last Edit: Feb 13, 2009, 09:09 pm by darudude Reason: 1
Really, I did not know that. What is the reason for it? I am trying to visualize it using Assembly code but the recursive function not being optimal really depends on how the assembly code is being written. Shows that you learn something new everyday!

@pracas. here is a non recursive function:

Code: [Select]

int powint(int x, int y)
{
 int val=x;
 for(int z=0;z<=y;z++)
 {
   if(z==0)
     val=1;
   else
     val=val*x;
 }
 return val;
}

halley

#6
Feb 13, 2009, 09:35 pm Last Edit: Feb 13, 2009, 09:38 pm by halley Reason: 1
darudude, there is no tail-recursion optimization in C.  Call a function, more stack is consumed.  At some point, the stack will grow so large it overwrites your non-stack variables, or vice versa, and kaboom.

Code: [Select]
long powint(int factor, unsigned int exponent)
{
   long product = 1;
   while (exponent--)
      product *= factor;
   return product;
}

kg4wsv

Function calls have: a return value, a return address, and parameters.  All these are stored on the stack.

In this example, we have a 2 byte return value, a 2 byte return address, and two 2 byte parameters.  That's 8 bytes per function call, so an absolute max of 128 iterations before the stack wraps (assuming you use no other RAM in your program; not likely but best case).

In addition, a function call and return (a jump and another jump) require as many as 4 cycles each, plus the overhead of storing and retrieving stack variables.  A compare and branch require 2 or 3 instructions in comparison.  (Note the compiler may optimize some of this badness away; this blathering is not the result of actually comparing compiler output, just looking at the instruction set.)

-j


kg4wsv

BTW, I'm not saying you shouldn't use recursion, just that you should be careful with it on a microcontroller.  If it will recurse more than a handful of times, you may need to think about a non-recursive solution.

-j


westfw

While everything everyone has said in true, and while it's silly to use the floating point pow() function here, you CAN fix your original code by causing the float to integer conversion to round instead of truncate:

Code: [Select]
k = k + (j[i]*pow(2,l));

Code: [Select]
k = k + 0.5 + (j[i]*pow(2,l));

There's a "standard" method of doing base conversion that distributes the power function over the digits.  See if you can figure it out:
Code: [Select]
result = 0;
for (i=0; i < NUMDIGITS; i++) {
 result = (result * BASE) + digits[i];
}


Avoid C's "," operator.  It's EEEEVIL!

Collin80

Quote
While everything everyone has said in true, and while it's silly to use the floating point pow() function here, you CAN fix your original code by causing the float to integer conversion to round instead of truncate:


And, while that is true, in this case using floating point code and rounding instead of using integer math is like mowing the lawn of your small condo with a brushhog. (or using a dump truck to haul a bag of dog food.) Totally wrong equipment for the operation at hand.

pracas

Thanks everyone! lot of information in here. I think i will stick to using the shift operator here.  ::)
Be The Change...

Go Up