Lisp: coerce float to rational


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.]


CL-USER> (coerce-float-to-rational 3.141592653589793)
CL-USER> (coerce-float-to-rational 3.141592653589793d0)

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)
CL-USER> (coerce-float-to-rational 1e20)

2 Responses to “Lisp: coerce float to rational”

  1. jaoswald Says:

    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.

  2. drj11 Says:

    @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.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: