Problem with pow function

Please have a look at this sketch and the following output.

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

  int length = 10;
  
  for(int i = 0; i < length; i++){
        
        float base = pow(2, i);
        
        Serial.print(base);
        Serial.print( " == ");
        Serial.print((long) base);
        Serial.print( " == ");
        Serial.println((int) base);
  }
}

void loop() {
}

Output:

1.00 == 1 == 1
2.00 == 2 == 2
4.00 == 3 == 3
8.00 == 7 == 7
16.00 == 15 == 15
32.00 == 31 == 31
64.00 == 63 == 63
128.00 == 127 == 127
256.00 == 255 == 255
512.00 == 511 == 511

Why do the values not equal?

There are much, much simpler ways of raising an integer to an integer power-of-two than using pow.
More accurate too.

GrooveFlotilla:
There are much, much simpler ways of raising an integer to an integer power-of-two than using pow.
More accurate too.

Please explain them with some examples or a link,

change

        Serial.print(base);

to

        Serial.print(base, 5);

and you will quickly see why.

This is not the first time this question has been asked on this forum.

This seems to be the classic problem of assuming that floating point arithmetic is exact. The pow() function uses floating point and you are truncating the result when, if you must use pow(), you should probably be rounding the result. Take a look at this.

What stowtie said.

Floating point arithmetic should never be treated as being exact. It can be exact in some very strict scenarios, but only then. A floating point value should always be treated like it has a tolerance.

What is happening here

128.00 == 127

is that the 128 value is actually 127.999999 - or something like that. The print function rounds this to two decimal places. The (int) cast always truncates.

For this reason, never use floating point for anything that must be counted exactly. In particular, time and currency. Use an integral value of the smallest unit you need.

PaulMurrayCbr:
In particular, time and currency. Use an integral value of the smallest unit you need.

For example, don't use a float to store the number of dollars. Use an integer to store the number of cents (assuming a decimal based currency system). Even if it's some wanky Imperial system with irregular conversions, you'll still want to go with integer values of the smallest unit. It might seems weird to store a price of 5 pounds as 4,800 farthings, but it's much easier for a computer to deal with numbers like that than it is for a human.

robtillaart:
Please explain them with some examples or a link,

Why? This topic has been discussed many times. OP can google, if he/she wants. The rest of us don't need to rehash the same discussion again.

Off topic, sorry

PaulS:
Why? This topic has been discussed many times. OP can google, if he/she wants. The rest of us don't need to rehash the same discussion again.

Very true, those discussions are probably drown in other threads, the point is valid. However have you thought about the possibility that maybe, yes maybe, they were not understandable for the OP?

Still if you react on a question, please be constructive (I know you can) all the time. Otherwise, why spend your energy on it?

The pow() function uses floating point and you are truncating the result when,

The executions of the following codes apparently indicate that the Compiler changes the evaluation chain for the formula pow(a, b) depending on the data type.

float base = pow(2, 4);
long *p;
p = (long*) &base;
long m = *p;
Serial.println(m, HEX);     //prints: 41800000h  -----> 16.0000....

Comment: The compiler uses binary32 (IEEE-754)/floating point format while evaluating pow(2, 4).

//--------------------------------------------------------------------------

long base = pow(2, 4);
long *p;
p = (long*) &base;
long m = *p;
Serial.println(m, HEX);    //prints: 10h -----> 16

Comment: The compiler adopts simple multiplication/repetitive additions/power series 
while evaluating pow(2, 4) and truncates the result to integer value (2E3.5 ---> 11).

The executions of the following codes apparently indicate that the Compiler changes the evaluation chain for the formula pow(a, b) depending on the data type.

I don’t see how it shows anything more than that the final store is different depending on the type that you’re storing to…
However, you’re correct that the compiler will evaluate pow() entirely at compile time if the arguments are constants, possibly leading to differences in the result, since the compiler probably uses a double-based version of pow() and runtime won’t. Here’s some disassembly:

volatile unsigned long m;

void f1() {
    float base = pow(10, 4);
    long *p;
    p = (long*) &base;
    m = *p;
   0:	80 e0       	ldi	r24, 0x00	; 0
   2:	90 e4       	ldi	r25, 0x40	; 64
   4:	ac e1       	ldi	r26, 0x1C	; 28
   6:	b6 e4       	ldi	r27, 0x46	; 70
   8:	80 93 00 00 	sts	0x0000, r24	; 0x800000 <__SREG__+0x7fffc1>
   c:	90 93 00 00 	sts	0x0000, r25	; 0x800000 <__SREG__+0x7fffc1>
  10:	a0 93 00 00 	sts	0x0000, r26	; 0x800000 <__SREG__+0x7fffc1>
  14:	b0 93 00 00 	sts	0x0000, r27	; 0x800000 <__SREG__+0x7fffc1>
  18:	08 95       	ret

0000001a <f2>:

void f2() {
    long base = pow(10, 4);
    long *p;
    p = (long*) &base;
    m = *p;
  1a:	80 e1       	ldi	r24, 0x10	; 16
  1c:	97 e2       	ldi	r25, 0x27	; 39
  1e:	a0 e0       	ldi	r26, 0x00	; 0
  20:	b0 e0       	ldi	r27, 0x00	; 0
  22:	80 93 00 00 	sts	0x0000, r24	; 0x800000 <__SREG__+0x7fffc1>
  26:	90 93 00 00 	sts	0x0000, r25	; 0x800000 <__SREG__+0x7fffc1>
  2a:	a0 93 00 00 	sts	0x0000, r26	; 0x800000 <__SREG__+0x7fffc1>
  2e:	b0 93 00 00 	sts	0x0000, r27	; 0x800000 <__SREG__+0x7fffc1>
  32:	08 95       	ret

My understanding:
1. Codes generated at compile time (Verify/Compile option of IDE) are different from those generated during upload phase of the IDE, which are essentially run-time codes.

2. The disassembly codes (of your post) probably compile-time codes; there is no trace of the base (10) and index(4) of pow(10, 4) formula in these codes; even, no indication of how the library function pow(a, b) would be called upon. I think that these are fixed at run-time (upload time).

3. In the given two versions of the disassembly codes, there are some differences as to the values assigned to r24-r27 registers. I think that these differences are due to different data types -- float and long.

GolamMostafa:
My understanding:
1. Codes generated at compile time (Verify/Compile option of IDE) are different from those generated during upload phase of the IDE, which are essentially run-time codes.

  1. All code is generated at verify/compile time. There is no code generated at upload time. It's the calculation that is either done at compile time by the PC the compiler is running on or at run-time by the processor on the arduino.

2 and 3 seem predicate upon your misunderstanding there.