简体   繁体   中英

Precision of a power of double in bison and avr-g++

I am writing a calculator for an avr microcontroller using bison and I have a problem with the resolution of a power of 2 doubles.

In my bison file I define the type as

    %define api.value.type {double}
    %token NUMBER

and then give the following rule

expr: NUMBER
     | expr '^' expr {$$ = pow($1, $3);}

And the code works properly except when I try to calculate 2^8 that gives me 255.9999 as the answer instead of 256 .

To see if the problem is with double or with pow I have modified the code this way:

expr: NUMBER
     | expr '^' expr {$$ = pow($1, $3);
                      double a = 2.0; double b = 8.0;
                      if (a == $1) lcd << "a ok";  // prints ok
                      if (b == $3) lcd << "b ok";  // prints ok
                      double c = pow(a, b);
                      lcd << c; // it shows 256!!!
                      if ($$ == c) lcd << "$$ ok";
                      else         lcd << "$$ wrong"; // prints wrong!!!!
                     }

As you can see the function pow works ok with a and b , and these two variables have the same value that $1 and $3 but $$ is different from c = pow(a, b) .

I don't know what is happening.

Its the first time I use bison so most probably I've done something wrong.

I am compiling with avr-g++ 9.2.0.

Thanks.


Edit: To see what is happening I've modified my rule in two different ways:

If I try:

expr: NUMBER
     | expr '^' expr {yyval = pow(2.0, 8.0);}

it gives me the right answer and print 256 .

But instead if I try:

expr: NUMBER
     | expr '^' expr {yyval = pow($1, $3);}

it gives me the wrong answer 255.9999

This has nothing to do with bison. The culprit is the math library on the AVR microcontroller.

When you write (in C):

double a = 2.0;
double b = 8.0;
double c = pow(a, b);

Gcc is smart enough to figure out that c will be 256.0. it's not necessary to do that computation at run time. Gcc just rewrites that to double c = 256.0; .

Gcc does that computation using the math library on the machine it is running on, or with its own bundled math library. That's likely to be the Gnu math library, which optimises the computation of small integer powers.

The other call to pow is computed at runtime, because the compiler can't know what $1 and $3 are going to be. So that call is done with the math library on the microcontroller, which is very slightly inaccurate. (It might do something like exp(8.0 * log(2.0)) , which introduces a small round-off error.)

One possible solution would be to write your own implementation of pow , which uses a more precise computation when the exponent is an integer.

avr-gcc double is 32 bits per default. For 64-bit double you'll need avr-gcc v10+, cf. the GCC release notes

https://gcc.gnu.org/gcc-10/changes.html#avr

https://gcc.gnu.org/wiki/avr-gcc#Libf7

The rounding and precision issues inherent to floating-point numbers will still be there, though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM