Enhancing decimal precision?

Hi all,

I need (well, would like) to calculate an 8th-degree polynomial with coefficients on the order of 10^-23. I understand the Arduino can only handle precision to 6 or 7 decimal places. Is there a library out there to enhance the precision of these calculations, or should I just do this calculation in post-processing via Excel?

Many thanks.

Well, since floating point numbers are essentially in scientific notation, if they're all small and don't need much precision, it would work.

WizenedEE:
Well, since floating point numbers are essentially in scientific notation, if they're all small and don't need much precision, it would work.

5.427964E-23 is pretty high precision, I think. Even using a ChipKit UNO32 initializing all data points as long doubles, my polynomial solves to 0, when theory says it needs to solve to 5.16335556227116E-27.

I had a look around out of curiosity and found a GCC soft float library, but i think it is still limited to the native data sizes.

Another interesting read was 'Arbitrary-precision arithmetic'

Verdris:
5.427964E-23 is pretty high precision, I think. Even using a ChipKit UNO32 initializing all data points as long doubles, my polynomial solves to 0, when theory says it needs to solve to 5.16335556227116E-27.

5.427964E-23 is just as precise as 5427964.

I'm curious why you need such high precision on a microcontroller.

pYro_65:
I had a look around out of curiosity and found a GCC soft float library, but i think it is still limited to the native data sizes.

Another interesting read was 'Arbitrary-precision arithmetic'

Ah, pYro_65, you got me started with that link. :slight_smile:

http://arduino.cc/forum/index.php/topic,85692.0.html

My optical systems use 405nm laser radiation as the basis for particle detection and at that wavelength, Rayleigh scattering is a huge factor. The polynomial I use is part of the algorithm my team uses to calculate such scattering down to the order of less than 1 Mm^-1 so we can eliminate that from our readings. It works fine on our larger systems, but they're controlled by PCs running LabView. For the portable instruments we use the Arduinos.

WizenedEE:

Verdris:
5.427964E-23 is pretty high precision, I think. Even using a ChipKit UNO32 initializing all data points as long doubles, my polynomial solves to 0, when theory says it needs to solve to 5.16335556227116E-27.

5.427964E-23 is just as precise as 5427964.

Then do you have any suggestions on how to solve this:

fk = ((5.427964 * pow(10,-23)) * pow(lambda,8)) - ((2.806622 * pow(10,-19)) * pow(lambda,7)) + ((6.207856 * pow(10,-16)) * pow(lambda,6)) - ((7.662177 * pow(10,-13)) * pow(lambda,5)) + ((5.767936 * pow(10,-10)) * pow(lambda,4)) - ((2.712549 * pow(10,-7)) * pow(lambda,3)) + ((7.799646 * pow(10,-5)) * pow(lambda,2)) - ((1.261146 * pow(10,-2)) * lambda) + 1.937987;

lambda is an int depending on the wavelength of the laser that's switched in for a given run. I could just do a LUT, but then there's this problem:

  f1 = 5791817 / (238.0185 - (1 / w2));
  f1 = f1 + (167909 / (57.362 - (1 / w2)));
  ref = 1 + (f1 * pow(10,-8));
  nu = 0.01 / (lambda * pow(10,-9));
  sig = (24 * pow(pi,3) * pow(nu,4) * (((ref * ref) - 1) / (((ref * ref) + 2) * ((ref * ref) + 2) * fk))) / (N0 * N0);

Which is evaluating to sig = 0 instead of 1.5881E-26 (as calculated by theory given appropriate parameters for pressure and wavelength).

Mixing types is not a good thing to do, when performing arithmetic operations.

f1 = 5791817 / (238.0185 - (1 / w2));

5791817 is an integer value, as is 1. Both will be interpreted as ints, which is OK for the 1, but not for 5791817.

You should be using 5791817.0 and 1.0.

It's also worth noting that 5791817.0 exceeds the number of digits that a float can accurately represent. Since the Arduino doesn't have a double type, you really are, I think, out of luck.

The link I gave above to the forum posting for arbitrary precision arithmetic should help. Whilst it won't be the fastest thing in the world, that's the trade-off you make: speed or precision. (Also high-precision numbers require quite a bit of progam memory).

On this page:

http://arduino.cc/forum/index.php/topic,85692.0.html

... I calculate e to 100 decimal places, for example.

I am planning to write, or find, a C++ wrapper for it to make it easier to use.

Does the lib support all the functions needed for Verdris?

  • bc_pow() bc_log() bc_sin() etc?

It does not appear to support the higher functions like that. I experimented with the library a while back:

Calculations like e, pi, sine can be done in smallish loops. Given the sine(x) function you can calculate cosine(x), and given both sine and cosine you can get the tangent.

Then I found an algorithm for raising e to a power, and taking natural logs. So armed with all that, you could do most stuff.

Whether it is practical is another thing. The big number stuff tends to be slow, and a memory hog. Memory fragmentation would be an issue. Also using 12 Kb or so of your 32 kB of program memory may or may not be more than you can afford.

Still, it provides a workable alternative to simple floats, you can crank the precision up as much as you want. If you stick to modest levels of precision you can probably calculate sines, etc. quite quickly, and avoid too much fragmentation.

maybe time for an I2C FPU for the Arduino :slight_smile: