Unexpected pow() behaviour

The following bit of code produces an unexpected result. When I pass a function parameter into the math.h power function, something goes wrong. In this example it returns 3 when 22 == 4 is expected.

#include <math.h>

void test(int a) {
  int b = (int) pow(2, a);
  Serial.println(b);            // Outputs 3
}

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

void loop() {}

It does spit out 4 when I replace a with the number 2 inside the function, so it can’t be a casting/rounding issue. What’s going on here?

It's not really unexpected. If you're only dealing with integer powers of integers, pow is a very poor choice.

AWOL: It's not really unexpected.

How is it not? pow(2, 2) returns 4 whilst pow(2, a) where a = 2 returns 3. That is odd behaviour.

AWOL: If you're only dealing with integer powers of integers, pow is a very poor choice.

What's the better solution?

Your 2 is a compile-time constant. The compiler is good at optimising those away.

For integers, a loop is simpler and more accurate For powers of 2, shifts are probably quicker - a table even quicker.

AWOL: Your 2 is a compile time constant. The compiler is good at optimising those away.

I don't get how an integer at compile time would be different from one being instantiated during run time. I tried compiling the same code (without the serial output) using gcc and TDM-GCC, and it both cases it did return 4.

AWOL: For integers, a loop is simpler and more accurate For powers of 2, shifts are probably quicker.

Ah yes, should have thought of bit shifting. Thanks.

In this example it returns 3 when 22 == 4 is expected.

In that case, the pow() function is probably returning a floating point value like 3.99999, which is 3 after converting to an integer.

I don’t get how an integer at compile time would be different from one being instantiated during run time

You could look at the generated assembly language.

A constant at compile time means the compiler can calculate the result, but a variable must be passed to “pow()” at runtime, where the vagaries of 32 bit floating point come into play.

It is never a good idea to pass in the wrong type of data or ignore what is returned. Change your code to:

#include <math.h>

void test(float a) {
  float b = pow(2.0, a);
  Serial.print("b = ");
  Serial.println(b);            // Outputs 3
}

void setup() {
  Serial.begin(115200);
  test(2.0);
}

void loop() {}

and the answer 4.0 is returned. Note that I passed 2.0 (a float) to test() and passed two floats to pow() and treated the return type as a float.

pow(float m, float e) converts the integers 2 and m to floats thereby possibly making rounding errors. Then it uses the formula

return exp( e * log(m));

which are three floating point operations, each with their possible rounding errors.

So pow() used for integers has at least 5 possible sources for error.

you can write a simple integer only power function e.g. ipow()

long ipow(int m, uint8_t e) // only positive integer exponents.  31 is max value.
{
  long rv = 1;
  for (; e >0; e--) rv *= m;
  return rv;
}

Tonitrum:
(…)
What’s the better solution?

Like someone said, shift is a better solution:

#include <math.h>

void test(int a) {
  int b = 0x01 << a;
  Serial.println(b);            // Outputs 3
}

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

void loop() {}

Serial.println(b); // Outputs 3

did you try that code ? ;)

Like someone said, shift is a better solution:

But only for integer powers of two or its multiples.

AWOL:

Like someone said, shift is a better solution:

But only for integer powers of two or its multiples.

It seams that is what we are talking about, right?:

  int b = (int) pow(2, a);

luisilva:

AWOL:

Like someone said, shift is a better solution:

But only for integer powers of two or its multiples.

It seams that is what we are talking about, right?:

  int b = (int) pow(2, a);

It seems that that is the original intention, but someone in the future reading this may assume that shifts are the universal solution. They're not even necessarily the complete solution here - "a" is, after all a signed integer.

Thanks for the answers guys. I only needed powers of two, so shifting is indeed a cleaner solution.

pow( ) is an almost useless function. In 30 years of writing C/C++, I have never found a good reason to use it myself.

The most common thing people use it for, is squaring numbers but there is really nothing wrong with x * x .

You're about the third person this month to have a similar sort of trouble with it.

michinyon: pow( ) is an almost useless function. In 30 years of writing C/C++, I have never found a good reason to use it myself.

Depends on the application, e.g. cuberoot (volume to side)

float cubeRoot(float x) { if (x >= 0) return pow(x, 1/3.0); return -pow(abs(x), 1/3.0); }

or physics formulas like the dew point

// DEWPOINT
// dewPoint function NOAA
// reference: http://wahiduddin.net/calc/density_algorithms.htm 
double dewPoint(double celsius, double humidity)
{
    double A0= 373.15/(273.15 + celsius);
    double SUM = -7.90298 * (A0-1);
    SUM += 5.02808 * log10(A0);
    SUM += -1.3816e-7 * (pow(10, (11.344*(1-1/A0)))-1) ;
    SUM += 8.1328e-3 * (pow(10,(-3.49149*(A0-1)))-1) ;
    SUM += log10(1013.246);
    double VP = pow(10, SUM-3) * humidity;
    double T = log(VP/0.61078);   // temp var
    return (241.88 * T) / (17.558-T);
}

Also good for converting watts to dBm’s and other logarithmic functions.