decimal floating point package

1.2k Views Asked by At

I understand the complexities when using binary (or binaryoid) floating point and representing the result in decimal:

1 (do ((numerator 1 (* 10 numerator)))
 2     ((>= numerator 1000000000000))
 3   (let ((fred (list 'coerce (/ numerator 3) (quote 'double-float))))
 4     (prin1 fred)
 5     (princ " ")
 6     (prin1 (eval fred))
 7     (terpri)))
(COERCE 1/3 'DOUBLE-FLOAT) 0.3333333333333333d0
(COERCE 10/3 'DOUBLE-FLOAT) 3.3333333333333335d0
(COERCE 100/3 'DOUBLE-FLOAT) 33.333333333333336d0
(COERCE 1000/3 'DOUBLE-FLOAT) 333.3333333333333d0
(COERCE 10000/3 'DOUBLE-FLOAT) 3333.3333333333335d0
(COERCE 100000/3 'DOUBLE-FLOAT) 33333.333333333336d0
(COERCE 1000000/3 'DOUBLE-FLOAT) 333333.3333333333d0
(COERCE 10000000/3 'DOUBLE-FLOAT) 3333333.3333333335d0
(COERCE 100000000/3 'DOUBLE-FLOAT) 3.3333333333333332d7
(COERCE 1000000000/3 'DOUBLE-FLOAT) 3.333333333333333d8
(COERCE 10000000000/3 'DOUBLE-FLOAT) 3.3333333333333335d9
(COERCE 100000000000/3 'DOUBLE-FLOAT) 3.3333333333333332d10

But I wish to avoid the infelicity of that final varying digit. Speed of computation is not important for me in this situation.

Do there exist any true decimal floating point LISP packages out there?

Edit 1: Ideally, this package would allow for arbitrary precision, as bignum does for integers.

Edit 2, in response to Dennis Jaheruddin's question:

[I]f you are not interested in the final digit but just want the numbers to be the same, you might just want to observe the first 15 or so digits?

I thought of that. It won't work. In the case of 2/3, for example, I'd want something like 666667. What I see is this:

 1 (do ((numerator 2 (* 10 numerator)))
 2     ((>= numerator 1000000000000))
 3   (let ((fred (list 'coerce (/ numerator 3) (quote 'double-float))))
 4     (prin1 fred)
 5     (princ " ")
 6     (prin1 (eval fred))
 7     (terpri)))
(COERCE 2/3 'DOUBLE-FLOAT) 0.6666666666666666d0
(COERCE 20/3 'DOUBLE-FLOAT) 6.666666666666667d0
(COERCE 200/3 'DOUBLE-FLOAT) 66.66666666666667d0
(COERCE 2000/3 'DOUBLE-FLOAT) 666.6666666666666d0
(COERCE 20000/3 'DOUBLE-FLOAT) 6666.666666666667d0
(COERCE 200000/3 'DOUBLE-FLOAT) 66666.66666666667d0
(COERCE 2000000/3 'DOUBLE-FLOAT) 666666.6666666666d0
(COERCE 20000000/3 'DOUBLE-FLOAT) 6666666.666666667d0
(COERCE 200000000/3 'DOUBLE-FLOAT) 6.6666666666666664d7
(COERCE 2000000000/3 'DOUBLE-FLOAT) 6.666666666666666d8
(COERCE 20000000000/3 'DOUBLE-FLOAT) 6.666666666666667d9
(COERCE 200000000000/3 'DOUBLE-FLOAT) 6.6666666666666664d10

As you can see, I can't even use the final digit to determine whether to round up; 64 rounds to 60, not 70. But I could discard the final digit and use the previous digit to round the rest of the number. I'm uncomfortable with this, though, because (a) I'm beginning to discard a lot of precision by this point, and (b) I'm not sure that there aren't cases where this would cause rounding the wrong way. A decimal floating point package, preferably with arbitrary precision, would be ideal.

Edit 3: As Rainer Joswig points out in his answer below, a non-portable potential solution would be to set the floating point precision. For those following along at home, he points out here that this is done thus: (SETF (EXT:LONG-FLOAT-DIGITS) n)

Edit 4: In the comments after his answer, Rainer Joswig recommends looking into the algebra systems Maxima and Axiom. Doing so leads to these excellent Wikipedia resources:

Edit 5: I have since determined that I do not need a decimal floating point package, but I'm still curious as to whether there is one. Probably there isn't.

Why don't I need one? The answer is a combination of (a) Rainer Joswig's pointer to the wu-decimal package and (b) wvxvw's mention of long division.

Although wu-decimal doesn't have the conventional characteristic and mantissa using base 10, it does introduce an intriguing idea: storing numbers as ratios. So 1/3 is stored as 1/3, not a repeating (for a finite length) binary fraction. Although multiplying numbers stored as ratios can soon yield quite lengthy ratios, the original precision is maintained throughout. I'm going to use that idea. What I don't need is wu-decimal's quite nifty parsing and writing of ratios as decimal numbers when appropriate, so I won't be installing the package. If you're interested in making easy the parsing and writing of such values, check out the package. (I haven't used it.)

What remains, then, is the printing of ratios as decimal numbers. For this I'll be using long division, just as wvxvw does. My code is somewhat different, but for the long division idea, I owe him many thanks.

1

There are 1 best solutions below

4
On

Never used it:

http://wukix.com/lisp-decimals

In GNU CLISP you can set the float precision.