1

I'm programming a calculator on an Arduino and I'm trying to calculate pow and writing it to a string (result). This is my code:

dtostrf(exp(n*log(x)), 0, 5, result); // x ^ n

2 ^ 2 = 4.00000 // works fine

10 ^ 5 = 99999.9770 // should be 100000

What's wrong with my code and how can I always get the right result? I mean how can I round it but still be able to use doubles ( e.g. 5.2 ^ 3.123 )

No Idea For Name
  • 11,411
  • 10
  • 42
  • 70
Akim Wright
  • 25
  • 1
  • 4
  • https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – deviantfan Nov 23 '14 at 13:34
  • See the answers to [this question](http://stackoverflow.com/questions/249467/what-is-a-simple-example-of-floating-point-rounding-error). – jolati Nov 23 '14 at 13:39
  • Arduino Uno has a very simple processor without any floating point support. A consequence is that *double* is not double precision at all, it takes 4 bytes and is emulated in software with single precision. The loss of precision is of course inevitable, you can never count on more than 6 precise digits. Finding solutions for problems like this is what keep Arduino enthusiasts busy. – Hans Passant Nov 23 '14 at 13:49
  • Ok, so that means I could use floats instead of doubles? – Akim Wright Nov 23 '14 at 13:50
  • No, a `float` is 4 bytes too. On a Due, a `double` will give more precision than a `float`; on an Uno, they'll be the same. A `float` will never have *more* precision than a `double`. – chiastic-security Nov 23 '14 at 14:19
  • In embedded real-time systems, CPU:s without floating-point unit used to be quite common, at least in the early 90s. Luckily, nowadays most CPU:s have floating point and many have vector units as well. Since your CPU does not, you could revert to methods like Newton-Raphson, I have seen that used in real-time systems for computing e.g. square roots on CPUs that have only integer arithmetic. Maybe you could do something similar for pow(). – Erik Alapää Nov 23 '14 at 14:21

2 Answers2

0

You're just hitting rounding errors. There's nothing you can do about this, except revert to an integer-based approach whenever the inputs are integers.

You could condition on whether the inputs are integers, and if so then use integer arithmetic; if not, then use doubles. But using exp and log will always introduce rounding errors, so you can't expect exact answers with that approach.

More precisely, to use integer arithmetic, you need the base to be an integer and the exponent to be a non-negative integer.

chiastic-security
  • 20,430
  • 4
  • 39
  • 67
0

Since you are programming a calculator, speed is not your concern but the number of reliable digits is. So, you could try to use a double precision library. It uses 64-Bit-doubles but has only about 200 FLOPS at 16MHz CPU clock and much less at higher-order calculations like exp(), log(), or sin(). Thus, it will take a second after having typed in the digits and pressed the enter button but this was also the case with the old 8-Bit-based pocket caluclators.

See this Link (only in German)

Hartmut Pfitzinger
  • 2,304
  • 3
  • 28
  • 48