Arduino Forum upgrade scheduled for Monday, October 20th, 11am-4pm (CEST). Sorry for the inconvenience!
Pages: [1]   Go Down
Author Topic: Weird but true pow(x,y) function  (Read 3761 times)
0 Members and 1 Guest are viewing this topic.
Chennai, India
Offline Offline
God Member
*****
Karma: 0
Posts: 752
Peace!!!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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;
}

 :-/
Logged

Be The Change...

Connecticut, US
Offline Offline
Edison Member
*
Karma: 2
Posts: 1036
Whatduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
« Last Edit: February 13, 2009, 02:09:22 pm by halley » Logged

Earth
Offline Offline
Sr. Member
****
Karma: 14
Posts: 334
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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.
« Last Edit: February 13, 2009, 02:23:00 pm by AdderD » Logged

0
Offline Offline
Sr. Member
****
Karma: 0
Posts: 356
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
int powint(int x, int y)
{
  if (y==0)
    return(1);
  else
    return(power(x,y-1)*x);
}
« Last Edit: February 13, 2009, 02:30:46 pm by darudude » Logged

0
Offline Offline
Faraday Member
**
Karma: 8
Posts: 2526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

0
Offline Offline
Sr. Member
****
Karma: 0
Posts: 356
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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;
}
« Last Edit: February 13, 2009, 03:09:14 pm by darudude » Logged

Connecticut, US
Offline Offline
Edison Member
*
Karma: 2
Posts: 1036
Whatduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
long powint(int factor, unsigned int exponent)
{
    long product = 1;
    while (exponent--)
       product *= factor;
    return product;
}
« Last Edit: February 13, 2009, 03:38:27 pm by halley » Logged

0
Offline Offline
Faraday Member
**
Karma: 8
Posts: 2526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

0
Offline Offline
Faraday Member
**
Karma: 8
Posts: 2526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 139
Posts: 6835
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
k = k + (j[i]*pow(2,l));

Code:
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:
result = 0;
for (i=0; i < NUMDIGITS; i++) {
  result = (result * BASE) + digits[i];
}

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

Earth
Offline Offline
Sr. Member
****
Karma: 14
Posts: 334
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Chennai, India
Offline Offline
God Member
*****
Karma: 0
Posts: 752
Peace!!!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks everyone! lot of information in here. I think i will stick to using the shift operator here.  :smiley
Logged

Be The Change...

Pages: [1]   Go Up
Arduino Forum upgrade scheduled for Monday, October 20th, 11am-4pm (CEST). Sorry for the inconvenience!
Jump to: