Re: [Sbcl-commits] master: Correctly round floats converted from ratios
Christophe Rhodes via Sbcl-devel <[email protected]>
| Newsgroups | gmane.lisp.steel-bank.devel |
|---|---|
| Message-ID | <[email protected]> |
stassats via Sbcl-commits <[email protected]> writes: > +(defun check-ratio-to-float (ratio type) > + (declare (ratio ratio)) > + (let* ((result (float ratio type)) > + (new-ratio (rational result))) > + (multiple-value-bind (sig exp sign) (integer-decode-float result) > + (let* ((prev-float (scale-float (float (* sign (1- sig)) type) exp)) I think this line is off-by-one if the float in question is a non-denormal and exactly at a power-of-two boundary, where the resolution is twice as fine below the float as above the float. One might say "well that just makes the test marginally weaker", but it's in exactly these cases that it's more likely that the implementation is wrong, so it would be nice for the test to be as strong as possible there. Or else some explicit tests near halfway points of [1,1+eps], [1-eps/2,1] and related? And some tests near least-positive-normalized-fooble-float? Also, how should conversion to float behave above most-positive-fooble-float? Is there an implicit interval above the maximum where we convert down and don't error, or does anything above the rational value of the most-positive float signal overflow? Christophe > + (next-float (scale-float (float (* sign (1+ sig)) type) exp)) > + (error (abs (- ratio new-ratio))) > + (error-prev (abs (- ratio (rational prev-float)))) > + (error-next (abs (- ratio (rational next-float))))) > + > + (cond > + ((< error-next error) > + (error "(float ~a ~a) = ~a, while ~a is closer" ratio type result next-float)) > + ((< error-prev error) > + (error "(float ~a ~a) = ~a, while ~a is closer" ratio type result prev-float)) > + ((or (= error error-prev) (= error error-next)) > + (unless (evenp sig) > + (error "(float ~a ~a) = ~a, not rounded to even" ratio type result)))))))) > + > +(with-test (:name :ratio-to-float) > + (let ((*random-state* (make-random-state t))) > + (loop repeat 50000 > + do > + (let* ((n-bits (random 100)) > + (d-bits (random 100)) > + (num (random (ash 1 n-bits))) > + (den (max 1 (random (ash 1 d-bits)))) > + (ratio (/ num den))) > + (when (typep ratio 'ratio) > + (check-ratio-to-float ratio 1f0) > + (check-ratio-to-float ratio 1d0)))))) > > ----------------------------------------------------------------------- > > > hooks/post-receive