Raymond Toy pushed to branch issue-504-read-denormals-with-rounding at cmucl / cmucl

Commits:

1 changed file:

Changes:

  • tests/float.lisp
    ... ... @@ -853,3 +853,176 @@
    853 853
       (ext:with-float-traps-masked (:underflow :inexact)
    
    854 854
         (assert-equal 0 (kernel:double-float-high-bits 1.1d-322))
    
    855 855
         (assert-equal #x16 (kernel:double-float-low-bits 1.1d-322))))
    
    856
    +
    
    857
    +(define-test float-ratio-float.denormal-double-rounding.single
    
    858
    +    (:tag :issues)
    
    859
    +  ;; Regression for the double-rounding bug exposed by reading
    
    860
    +  ;; "7.290983e-39".  The exact rational lies at 5203019.332 * 2^-149,
    
    861
    +  ;; below halfway between denormals #x4f644b and #x4f644c.  An earlier
    
    862
    +  ;; fix rounded to 24 bits first (lifting it to the artificial tie
    
    863
    +  ;; 5203019.5 * 2^-149) and then re-rounded ties-to-even to #x4f644c.
    
    864
    +  ;; FLOAT-RATIO-FLOAT now re-rounds directly to denormal precision in a
    
    865
    +  ;; single step, yielding the correctly-rounded #x4f644b.
    
    866
    +  (assert-equal #x4f644b
    
    867
    +		(kernel:single-float-bits 7.290983e-39)))
    
    868
    +
    
    869
    +(define-test float-ratio-float.denormal-low-below-halfway.single
    
    870
    +    (:tag :issues)
    
    871
    +  ;; Exercise ROUND-DENORMAL's `low < halfway' branch via FLOAT-RATIO-
    
    872
    +  ;; FLOAT.  Value 11/10 * least-positive is 1.1 denormal-units; the
    
    873
    +  ;; loop sees several bits of low and the comparison must take the
    
    874
    +  ;; round-down path so the mantissa stays at 1.
    
    875
    +  (let ((x (* 11/10 (expt 2 -149))))
    
    876
    +    (assert-equal #x00000001
    
    877
    +		  (kernel:single-float-bits
    
    878
    +		   (kernel::float-ratio-float x 'single-float))
    
    879
    +		  x)))
    
    880
    +
    
    881
    +(define-test float-ratio-float.denormal-low-above-halfway.single
    
    882
    +    (:tag :issues)
    
    883
    +  ;; ROUND-DENORMAL's `low > halfway' branch: 17/10 * least-positive
    
    884
    +  ;; (= 1.7 denormal-units), past halfway between 1 and 2, rounds up.
    
    885
    +  (let ((x (* 17/10 (expt 2 -149))))
    
    886
    +    (assert-equal #x00000002
    
    887
    +		  (kernel:single-float-bits
    
    888
    +		   (kernel::float-ratio-float x 'single-float))
    
    889
    +		  x)))
    
    890
    +
    
    891
    +(define-test float-ratio-float.denormal-tie-to-even.single
    
    892
    +    (:tag :issues)
    
    893
    +  ;; ROUND-DENORMAL's tie path, REM = 0: exact halfway between two
    
    894
    +  ;; denormals rounds to even.  3/2 * least-positive ties between
    
    895
    +  ;; denormals 1 and 2; the even neighbour is 2.
    
    896
    +  (let ((x (* 3/2 (expt 2 -149))))
    
    897
    +    (assert-equal #x00000002
    
    898
    +		  (kernel:single-float-bits
    
    899
    +		   (kernel::float-ratio-float x 'single-float))
    
    900
    +		  x))
    
    901
    +  ;; 5/2 * least-positive ties between 2 and 3; even is 2.
    
    902
    +  (let ((x (* 5/2 (expt 2 -149))))
    
    903
    +    (assert-equal #x00000002
    
    904
    +		  (kernel:single-float-bits
    
    905
    +		   (kernel::float-ratio-float x 'single-float))
    
    906
    +		  x))
    
    907
    +  ;; 7/2 * least-positive ties between 3 and 4; even is 4.
    
    908
    +  (let ((x (* 7/2 (expt 2 -149))))
    
    909
    +    (assert-equal #x00000004
    
    910
    +		  (kernel:single-float-bits
    
    911
    +		   (kernel::float-ratio-float x 'single-float))
    
    912
    +		  x))
    
    913
    +  ;; 1/2 * least-positive ties between 0 and 1; even is 0.
    
    914
    +  (let ((x (* 1/2 (expt 2 -149))))
    
    915
    +    (assert-equal #x00000000
    
    916
    +		  (kernel:single-float-bits
    
    917
    +		   (kernel::float-ratio-float x 'single-float))
    
    918
    +		  x)))
    
    919
    +
    
    920
    +(define-test float-ratio-float.denormal-tie-with-sticky.single
    
    921
    +    (:tag :issues)
    
    922
    +  ;; ROUND-DENORMAL's tie path with REM != 0: when the loop's
    
    923
    +  ;; FRACTION-AND-GUARD reaches an exact halfway pattern but the
    
    924
    +  ;; division has a nonzero remainder, the rounding must go up because
    
    925
    +  ;; the original rational is strictly above halfway.  3/2 * least-
    
    926
    +  ;; positive + a tiny fraction of least-positive lands just past tie 1-2,
    
    927
    +  ;; rounds up to 2.
    
    928
    +  (let ((x (+ (* 3/2 (expt 2 -149))
    
    929
    +	      (* (expt 2 -149) 1/1000000000))))
    
    930
    +    (assert-equal #x00000002
    
    931
    +		  (kernel:single-float-bits
    
    932
    +		   (kernel::float-ratio-float x 'single-float))
    
    933
    +		  x))
    
    934
    +  ;; 1/2 + tiny: just past tie 0-1, rounds up to 1.
    
    935
    +  (let ((x (+ (* 1/2 (expt 2 -149))
    
    936
    +	      (* (expt 2 -149) 1/1000000000))))
    
    937
    +    (assert-equal #x00000001
    
    938
    +		  (kernel:single-float-bits
    
    939
    +		   (kernel::float-ratio-float x 'single-float))
    
    940
    +		  x)))
    
    941
    +
    
    942
    +(define-test float-ratio-float.denormal-excess.single
    
    943
    +    (:tag :issues)
    
    944
    +  ;; Exercise DENORMAL-EXCESS over a range of magnitudes.  EXCESS
    
    945
    +  ;; varies inversely with the result's magnitude; each case has the
    
    946
    +  ;; result land on a chosen denormal so an off-by-one in the EXCESS
    
    947
    +  ;; computation would shift the result by a factor of two.
    
    948
    +  ;; EXCESS = 1: value near smallest normal; pick (2^23 - 1) * 2^-149,
    
    949
    +  ;; the largest denormal.
    
    950
    +  (let ((x (* (1- (expt 2 23)) (expt 2 -149))))
    
    951
    +    (assert-equal #x007fffff
    
    952
    +		  (kernel:single-float-bits
    
    953
    +		   (kernel::float-ratio-float x 'single-float))
    
    954
    +		  x))
    
    955
    +  ;; EXCESS = 4: small denormal, mantissa 2^19.
    
    956
    +  (let ((x (expt 2 -130)))
    
    957
    +    (assert-equal (ash 1 19)
    
    958
    +		  (kernel:single-float-bits
    
    959
    +		   (kernel::float-ratio-float x 'single-float))
    
    960
    +		  x))
    
    961
    +  ;; EXCESS = 21: very small denormal, mantissa 8.
    
    962
    +  (let ((x (* 8 (expt 2 -149))))
    
    963
    +    (assert-equal #x00000008
    
    964
    +		  (kernel:single-float-bits
    
    965
    +		   (kernel::float-ratio-float x 'single-float))
    
    966
    +		  x))
    
    967
    +  ;; EXCESS = 23 (the maximum): only the bottom three denormals are
    
    968
    +  ;; reachable.  3 * least-positive gives mantissa 3.
    
    969
    +  (let ((x (* 3 (expt 2 -149))))
    
    970
    +    (assert-equal #x00000003
    
    971
    +		  (kernel:single-float-bits
    
    972
    +		   (kernel::float-ratio-float x 'single-float))
    
    973
    +		  x)))
    
    974
    +
    
    975
    +(define-test float-ratio-float.denormal-carry-to-normal.single
    
    976
    +    (:tag :issues)
    
    977
    +  ;; DENORMAL-FROM-BITS's carry-into-smallest-normal branch.  Rounding
    
    978
    +  ;; promotes the denormal mantissa to 2^(DIGITS-1), which doesn't fit
    
    979
    +  ;; in the denormal's stored mantissa width; the result must be the
    
    980
    +  ;; smallest normal (stored exponent = 1, mantissa = 0).
    
    981
    +  ;;
    
    982
    +  ;; (2^24 - 1)/2 * 2^-149 is an exact tie between the largest denormal
    
    983
    +  ;; (mantissa 2^23 - 1) and the smallest normal (2^-126); the latter
    
    984
    +  ;; has stored mantissa 0 which is even, so the tie rounds to it.
    
    985
    +  (let ((x (* (1- (expt 2 24)) 1/2 (expt 2 -149))))
    
    986
    +    (assert-equal #x00800000
    
    987
    +		  (kernel:single-float-bits
    
    988
    +		   (kernel::float-ratio-float x 'single-float))
    
    989
    +		  x))
    
    990
    +  ;; Just past that tie, also rounds up to smallest normal.
    
    991
    +  (let ((x (+ (* (1- (expt 2 24)) 1/2 (expt 2 -149))
    
    992
    +	      (* (expt 2 -149) 1/1000))))
    
    993
    +    (assert-equal #x00800000
    
    994
    +		  (kernel:single-float-bits
    
    995
    +		   (kernel::float-ratio-float x 'single-float))
    
    996
    +		  x))
    
    997
    +  ;; Just below that tie, rounds down to the largest denormal.
    
    998
    +  (let ((x (- (* (1- (expt 2 24)) 1/2 (expt 2 -149))
    
    999
    +	      (* (expt 2 -149) 1/1000))))
    
    1000
    +    (assert-equal #x007fffff
    
    1001
    +		  (kernel:single-float-bits
    
    1002
    +		   (kernel::float-ratio-float x 'single-float))
    
    1003
    +		  x)))
    
    1004
    +
    
    1005
    +(define-test float-ratio-float.denormal-double-rounding.double
    
    1006
    +    (:tag :issues)
    
    1007
    +  ;; Double-float equivalents.  Pick a rational that lands strictly
    
    1008
    +  ;; below halfway between two denormals after 53-bit rounding would
    
    1009
    +  ;; otherwise lift to the halfway position.  Verify a few denormal
    
    1010
    +  ;; cases also work end-to-end through FLOAT-RATIO-FLOAT.
    
    1011
    +  ;;
    
    1012
    +  ;; 1.1d-322 -> mantissa #x16 (= 22).
    
    1013
    +  (assert-equal 0 (kernel:double-float-high-bits 1.1d-322))
    
    1014
    +  (assert-equal #x16 (kernel:double-float-low-bits 1.1d-322))
    
    1015
    +  ;; Tie-to-even, double: 3/2 * least-positive ties between denormals
    
    1016
    +  ;; 1 and 2; even neighbour is 2.
    
    1017
    +  (let ((x (* 3/2 (expt 2 -1074))))
    
    1018
    +    (assert-equal 0 (kernel:double-float-high-bits
    
    1019
    +		     (kernel::float-ratio-float x 'double-float)))
    
    1020
    +    (assert-equal 2 (kernel:double-float-low-bits
    
    1021
    +		     (kernel::float-ratio-float x 'double-float))))
    
    1022
    +  ;; Carry into smallest normal: (2^53 - 1)/2 * 2^-1074 exactly halfway
    
    1023
    +  ;; between the largest double denormal and the smallest normal;
    
    1024
    +  ;; rounds up to the smallest normal #x00100000_00000000.
    
    1025
    +  (let* ((x (* (1- (expt 2 53)) 1/2 (expt 2 -1074)))
    
    1026
    +	 (r (kernel::float-ratio-float x 'double-float)))
    
    1027
    +    (assert-equal #x00100000 (kernel:double-float-high-bits r) x)
    
    1028
    +    (assert-equal 0 (kernel:double-float-low-bits r) x)))