Jérôme Radix
Jérôme Radix

Reputation: 10533

How do I round a float number to a certain number of digits, in a portable way

Most of the time when someone asks how to round a float to a certain number of digits, the classic answer given is to use FORMAT :

(format nil "~,2F" 6.376)
==> "6.38"

However, in a particular case, the rounding using FORMAT is implementation dependant !

In CLHS - 22.3.3.1 Tilde F: Fixed-Format Floating-Point :

When rounding up and rounding down would produce printed values equidistant from the scaled value of arg, then the implementation is free to use either one. For example, printing the argument 6.375 using the format ~4,2F may correctly produce either 6.37 or 6.38.

Moreover, ffloor does not seem to me to allow rounding to several digits after the decimal point.

So my question is : How do you explicitly round the number 6.375 to 6.37 in a portable way ?

Upvotes: 2

Views: 766

Answers (1)

ignis volens
ignis volens

Reputation: 9252

As you say, the answer is to use numerical functions, not I/O ones.

The obvious (indeed, pretty much the only) way to do this is by multiplying the number by 10^n, then rounding to integer and then dividing by 10^n. In CL the various rounding functions are specified in a way which makes this a little easier:

(defun fround-to-n-digits (v &optional (n 0))
  ;; These declarations are really to show intent: FLOAT is not enough
  ;; to optimize although perhaps a good type-inferencing compiler can
  ;; work out that this takes double->double &c.
  (declare (type float v)
           (type (integer 0) n))
  (let ((10^-n (expt 10 (- n))))
    (* (fround v 10^-n)
       10^-n)))

round and fround are defined to round to even in the usual way, truncate rounds towards zero, floor rounds down, ceiling rounds up.

However note that this will certainly run into float-representation issues the moment you look at it even slightly hard: such is the way with any floating-point maths. In particular if you assume that (fround-to-n-digits x 2) will always print some nice thing with two decimal digits you're in for an unpleasant surprise.

Upvotes: 3

Related Questions