Reputation: 10533
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
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