Pages: [1]   Go Down
Author Topic: strange pow() results [solved]  (Read 871 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,

I am trying to use a for loop to output 1, 2, 4, 8, 16, ..., 128 out of the serial port. My approach was
Code:
for (int i=0; i <= 7; i++) {

  outPut = Pow(2, i);
  SPI.transfer(outPut);
  delay(100);
}

Somehow this gives me 1, 2, 3, 7, 15..., 127. Is this a bug or did I miss something declaring the variables?
(whether outPut was an int or byte didn't make a difference)

I know, I could always declare an array with the wanted numbers and do something like
Code:
SPI.transfer(outPut[i]);
but at some point in my work this array would have 200 numbers and it doesn't make much sense to me...

Thx
ff
« Last Edit: May 10, 2011, 03:29:06 am by amphibious » Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 547
Posts: 45982
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Somehow this gives me 1, 2, 3, 7, 15..., 127. Is this a bug or did I miss something declaring the variables?
Did you look at the declaration for the pow function? Did you notice that it takes float/double arguments, and returns a float/double value?

Did you notice that the value gets truncated when stored in an int? Yes, you did.
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 238
Posts: 24298
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I am trying to use a for loop to output 1, 2, 4, 8, 16, ..., 128

Code:
for (int i=0; i <= 7; i++)
  Serial.println (1 << i);
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 435
Posts: 23611
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You really just need to bitshift a 1 across your variable, something like
x=1
x=x<<1, now x=b10
x=x<<1, now x=b100
x=x<<1, now x=b1000

http://arduino.cc/en/Reference/Bitshift
Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks guys!
Sorry, I'm not used to dealing with different kinds of variables yet. Will pay more attention in the future.
Logged

Left Coast, USA
Offline Offline
Sr. Member
****
Karma: 5
Posts: 499
Sometimes I just can't help myself.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

...
Somehow this gives me 1, 2, 3, 7, 15..., 127...
Code:
SPI.transfer(outPut[i]);
but at some point in my work this array would have 200 numbers and it doesn't make much sense to me...

Thx
ff

The library function pow() operates on floating point arguments and returns a floating point value.  It may turn out that 2.0 to the power 2.0 calculates out to be something slightly less than 4.0.

To round a positive floating point value to the nearest integer, add  0.5 first.

So it could go like this:
Code:
   unsigned int outPut;
    for (int i=0; i < 16; i++) {

        outPut = pow(2, i)+0.5; // Round off floating point to nearest integer
        Serial.print(outPut);
.
.
.

However... And This Is Important:

If you want integer powers of an integer, there is really no need to go through all of the floating point stuff.  (I know, I know, the presence of a library function tries to protect you from actually thinking about what is going on, but the floating point stuff is horribly inefficient in terms of code size as well as run time).

A general positive-integer-to-positive-integer power function could look like this:
Code:
unsigned int Pow(unsigned int base, byte ex)
{
    unsigned retval = 1;
    for (unsigned i = 0; i < ex; i++) {
        retval *= base;
    }
    return retval;
}

If you only need powers of 2, it's even simpler:
Code:
unsigned int Pow2(byte ex)
{
    return 1 << ex;
}


So, to compare results, consider something like the following sketch:
Code:
void setup()
{
    Serial.begin(9600);
}
void loop()
{
    unsigned int outPut;
    for (int i=0; i < 16; i++) {

        outPut = pow(2, i) + 0.5; // Round off floating point to nearest integer
        Serial.print(outPut);
        
        Serial.print("  ");
        outPut = Pow(2, i);    // Integer raised to an integer power
        Serial.print(outPut);
        
        outPut = Pow2(i);      // 2 raised to an integer power
        Serial.print("  ");
        Serial.println(outPut);
        delay(100);
    }
    delay(10000);
}
// Integer power to integer function:
unsigned int Pow(unsigned int base, byte ex)
{
// Stuff I showed above
}

// Power of 2
unsigned int Pow2(byte ex)
{
// Stuff I showed above
}


Output:


1  1  1
2  2  2
4  4  4
8  8  8
16  16  16
32  32  32
64  64  64
128  128  128
256  256  256
512  512  512
1024  1024  1024
2048  2048  2048
4096  4096  4096
8192  8192  8192
16384  16384  16384
32768  32768  32768


Regards,

Dave

Footnote:
Even though we don't recommend the floating point function for simple integer powers, it's important to realize that whenever floating point operations are involved there is always the possibility of roundoff error.  Converting floating point values to integer values always truncates the fractional part, so, for example a floating point number like 3.999999 becomes 3 if you just assign it to an integer variable.  Rounding of positive values is easily performed by adding 1/2 before truncating.

And, by the way, case is important in identifiers:  pow() is not the same as Pow().  Generally speaking, it is best to paste the actual code that you are asking about into your post.
« Last Edit: May 09, 2011, 01:32:56 pm by davekw7x » Logged

Central MN, USA
Offline Offline
Tesla Member
***
Karma: 64
Posts: 6889
Phi_prompt, phi_interfaces, phi-2 shields, phi-panels
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Dave. That same problem troubled me this afternoon. A new challenge would be negative floating point number to negative integer. Remember abs() is defined strangely and can't handle operations within its parentheses. Printing floating numbers on lcd is just another challenge.
Logged


Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 168
Posts: 12425
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Quote
Printing floating numbers on lcd is just another challenge.
Give this a try
Code:
#include <stdlib.h>
char *float2s(float f, unsigned int digits=2)
{
  static char buf[16];
  return dtostre(f, buf, digits, 1);
}

....
lcd.print(float2s(f, 4));
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks everybody!!!

I changed the topic to a more fitting title and declared it solved. Learned quite a lot today!
Logged

Left Coast, USA
Offline Offline
Sr. Member
****
Karma: 5
Posts: 499
Sometimes I just can't help myself.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

... negative floating point number to negative integer....
I'm not sure what the challenge is.  I mean you could create a function like the simple Pow() that I showed except make the first argument a float and the second argument an int.

Then inside the function, use abs() on the second argument to calculate the power.  If the second argument is less than zero, return 1.0 divided by the result.  (Or you could have one loop like the one that I showed to calculate value for non-negative integer powers and another loop that divides instead of multiplies each time through the loop.)

However...

Since we are now dealing with floating point numbers, there will be floating point arithmetic, and we must accept the possibility that roundoff errors might occur.  The whole point of the previous discussion was to eliminate the overhead of floating point numbers and eliminate the possibility of roundoff errors by treating everything as ints.  See Footnote.

Furthermore...

The library function pow() is implemented in such a way that it does the "right thing" for integer powers of negative numbers, so you don't need anything special.
Code:
// Calculating negative and positive powers
// of negative floating point numbers
//
// davekw7x

//
//Function to express floating point number in a C-Style "string"
//
// by robtillaart
//
char *float2s(float f, unsigned int digits=2);

void setup()
{
    Serial.begin(9600);
    
}

void loop(){
    float x = -1.2345678e10;
    Serial.print("x = ");
    Serial.println(float2s(x, 6));
    Serial.println();
    for (int i = -3; i < 4; i++) {
        float y = pow(x, i);
        Serial.print("i = ");Serial.print(i);Serial.print(": ");
        Serial.print(float2s(y, 6));
        Serial.print("  ");
        y = Pow(x, i);
        Serial.println(float2s(y,6));
    }
    delay(10000);

}
// There is no particular advantage to having our own
// function
float Pow(float base, int ex)
{
    float retval = 1.0;
    int y = abs(ex);
    for (unsigned i = 0; i < y; i++) {
        retval *= base;
    }
    if (ex < 0) {
        retval = 1.0 / retval;
    }
    return retval;
}
    

char *float2s(float f, unsigned int digits)
{
  static char buf[16];
  return dtostre(f, buf, digits, 1);
}

Output:

x = -1.234568e+10

i = -3: -5.314434e-31  -5.314411e-31
i = -2:  6.561020e-21   6.561001e-21
i = -1: -8.100012e-11  -8.100001e-11
i = 0:  1.000000e+00   1.000000e+00
i = 1: -1.234566e+10  -1.234568e+10
i = 2:  1.524153e+20   1.524158e+20
i = 3: -1.881668e+30  -1.881676e+30




Footnote:
Of course, with integer arithmetic we can get exact values for positive powers up to the point where overflow occurs.  Floating point arithmetic has a wider range of numbers that can be represented, but we must always be prepared to accept the possibility of roundoff errors.  The particular example of the original post had values that could be calculated exactly with integer arithmetic.

In addition to the limited range of integers compared with floating point numbers there is another limitation that I did not bring up:  With integer arithmetic it not very easy to detect overflow.

Furthermore, how would the function be able to convey the overflow information to the calling function.  (Remember that avr-gcc does not support exceptions.)

On the other hand, with properly implemented floating point numbers arithmetic, the calculations will "automatically" detect overflow and yield a result of inf or -inf, or, in some cases, nan.  Try calculating PI to the power 80
« Last Edit: May 10, 2011, 09:46:43 am by davekw7x » Logged

Pages: [1]   Go Up
Jump to: