Search code examples
floating-pointlispcommon-lisp

Floating point rounding error when computing the power of a double-float number


I want to develop a Common Lisp solution to Leetcode problem 50. "Pow(x, n)" which asks:

"Implement pow(x, n), which calculates x raised to the power n (i.e., x^n)."

My direct solution is :

(defun pow (x n)
  (declare (ftype (function (double-float fixnum) double-float) pow))
  (if (zerop n)
      1d0
      (let ((r 1d0))
        (dotimes (i (abs n)) (setf r (* r x)))
        (if (plusp n)
            r
            (/ 1d0 r)))))

(pow 2.1d0 3)
=> 9.261000000000001d0

Why does it have a 1 at the 15th digit after period ? How can I get the result without this rounding error ?

My Common Lisp implementation : SBCL 2.3.4 on linux.


Solution

  • Calculating with floats is not exact. Even representing numbers as floats is not exact. 1/3 is 0.3333..., where a float on a computer has limited precision.

    In Common Lisp one can compute with integers and rational numbers.

    Your code, but more general:

    (defun pow (x n)
      (if (zerop n)
          1
          (let ((r 1))
            (dotimes (i (abs n)) (setf r (* r x)))
            (if (plusp n)
                r
                (/ 1 r)))))
    

    Now your 2.1 might mean 21/10, which is a number in Common Lisp.

    CL-USER 7 > (pow 21/10 3)
    9261/1000
    

    We can then convert the result to a double float:

    CL-USER 8 > (float * 1.0d0)
    9.261D0