Since all floats are mathematical rationals you might expect to be able to coerce a float to a rational. But you can’t:
* (coerce 1d0 'rational) debugger invoked on a SIMPLE-TYPE-ERROR: 1.0d0 can't be converted to type RATIONAL.
Here’s a function which does what (coerce x 'rational)
might do:
(defun coerce-float-to-rational (x) (declare (type float x)) (let ((b (float-radix x))) (multiple-value-bind (signif e s) (integer-decode-float x) (* s signif (expt b e)))))
[Edit on 2007-07-12: jaoswald points out that this functionality is provided by the function rational
. So my code could be (part of) an implementation of rational
.]
Example:
CL-USER> (coerce-float-to-rational 3.141592653589793) 13176795/4194304 CL-USER> (coerce-float-to-rational 3.141592653589793d0) 884279719003555/281474976710656
Numbers that are mathematical integers (which includes all sufficiently large ones) come out as integer objects. Note that for very large numbers you quickly exhaust the precision of the underlying float type:
CL-USER> (coerce-float-to-rational 1e10) 10000000000 CL-USER> (coerce-float-to-rational 1e20) 100000002004087734272
2007-07-12 at 19:51:16
Look in the Hyperspec permuted symbol index under “R” for “Rational.” You can follow the links to the functions RATIONAL and RATIONALIZE.
(rational 1e20) will give you a precise rational value, (rationalize 1e20) will be more likely to get a “round” value.
http://www.lisp.org/HyperSpec/Body/fun_rationalcm_rationalize.html
2007-07-13 at 08:26:06
@jaoswald: Thanks for that. I’m a bit of a Lisp novice, so it’s easy for me to not find or simply not know about stuff. I’ve edited the article to add a link.