Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
-
20b8c4eb
by Raymond Toy at 2026-06-26T14:12:04-07:00
-
5926490b
by Raymond Toy at 2026-06-26T14:12:04-07:00
3 changed files:
Changes:
| ... | ... | @@ -885,40 +885,57 @@ |
| 885 | 885 | ;;; denormalized or underflows to 0.
|
| 886 | 886 | ;;;
|
| 887 | 887 | (defun scale-float-maybe-underflow (x exp)
|
| 888 | - (multiple-value-bind (sig old-exp)
|
|
| 889 | - (integer-decode-float x)
|
|
| 888 | + (declare (type (or single-float double-float) x)
|
|
| 889 | + (fixnum exp))
|
|
| 890 | + (multiple-value-bind (sig old-exp float-sign)
|
|
| 891 | + (integer-decode-float x)
|
|
| 890 | 892 | (let* ((digits (float-digits x))
|
| 893 | + (1+digits (1+ digits))
|
|
| 891 | 894 | (new-exp (+ exp old-exp digits
|
| 892 | 895 | (etypecase x
|
| 893 | 896 | (single-float vm:single-float-bias)
|
| 894 | 897 | (double-float vm:double-float-bias))))
|
| 895 | - (sign (if (minusp (float-sign x)) 1 0)))
|
|
| 898 | + (sign (if (minusp float-sign) 1 0)))
|
|
| 896 | 899 | (cond
|
| 897 | - ((< new-exp
|
|
| 898 | - (etypecase x
|
|
| 899 | - (single-float vm:single-float-normal-exponent-min)
|
|
| 900 | - (double-float vm:double-float-normal-exponent-min)))
|
|
| 901 | - (when (vm:current-float-trap :inexact)
|
|
| 902 | - (error 'floating-point-inexact :operation 'scale-float
|
|
| 903 | - :operands (list x exp)))
|
|
| 904 | - (when (vm:current-float-trap :underflow)
|
|
| 905 | - (error 'floating-point-underflow :operation 'scale-float
|
|
| 906 | - :operands (list x exp)))
|
|
| 907 | - (let ((shift (1- new-exp)))
|
|
| 908 | - ;; Is it necessary to have this IF here? Is there any case
|
|
| 909 | - ;; where (ash sig shift) won't return 0 when
|
|
| 910 | - ;; shift < -(digits-1)?
|
|
| 911 | - (if (< shift (- (1- digits)))
|
|
| 900 | + ((< new-exp
|
|
| 901 | + (etypecase x
|
|
| 902 | + (single-float vm:single-float-normal-exponent-min)
|
|
| 903 | + (double-float vm:double-float-normal-exponent-min)))
|
|
| 904 | + (when (vm:current-float-trap :inexact)
|
|
| 905 | + (error 'floating-point-inexact :operation 'scale-float
|
|
| 906 | + :operands (list x exp)))
|
|
| 907 | + (when (vm:current-float-trap :underflow)
|
|
| 908 | + (error 'floating-point-underflow :operation 'scale-float
|
|
| 909 | + :operands (list x exp)))
|
|
| 910 | + ;; To round correctly, let the hardware multiplier do the
|
|
| 911 | + ;; rounding: build a normal float whose stored exponent is
|
|
| 912 | + ;; bumped up by 1+DIGITS (which puts it safely in the normal
|
|
| 913 | + ;; range), then multiply by 2^-(1+DIGITS). The multiplier is
|
|
| 914 | + ;; an exact power of two, so the multiplication is exact
|
|
| 915 | + ;; apart from the unavoidable rounding step that expresses
|
|
| 916 | + ;; the product as a denormal, which the FPU performs in the
|
|
| 917 | + ;; current rounding mode. If the bumped exponent is zero or
|
|
| 918 | + ;; negative the bumped float would itself be a denormal --
|
|
| 919 | + ;; losing the implicit 1 bit of SIG -- so handle that case
|
|
| 920 | + ;; explicitly by returning signed zero.
|
|
| 921 | + (let ((bumped-exp (+ new-exp 1+digits)))
|
|
| 922 | + (cond
|
|
| 923 | + ((<= bumped-exp 0)
|
|
| 912 | 924 | (etypecase x
|
| 913 | 925 | (single-float (single-from-bits sign 0 0))
|
| 914 | - (double-float (double-from-bits sign 0 0)))
|
|
| 926 | + (double-float (double-from-bits sign 0 0))))
|
|
| 927 | + (t
|
|
| 915 | 928 | (etypecase x
|
| 916 | - (single-float (single-from-bits sign 0 (ash sig shift)))
|
|
| 917 | - (double-float (double-from-bits sign 0 (ash sig shift)))))))
|
|
| 918 | - (t
|
|
| 919 | - (etypecase x
|
|
| 920 | - (single-float (single-from-bits sign new-exp sig))
|
|
| 921 | - (double-float (double-from-bits sign new-exp sig))))))))
|
|
| 929 | + (single-float
|
|
| 930 | + (* (single-from-bits sign bumped-exp sig)
|
|
| 931 | + (scale-float 1f0 (- 1+digits))))
|
|
| 932 | + (double-float
|
|
| 933 | + (* (double-from-bits sign bumped-exp sig)
|
|
| 934 | + (scale-float 1d0 (- 1+digits)))))))))
|
|
| 935 | + (t
|
|
| 936 | + (etypecase x
|
|
| 937 | + (single-float (single-from-bits sign new-exp sig))
|
|
| 938 | + (double-float (double-from-bits sign new-exp sig))))))))
|
|
| 922 | 939 | |
| 923 | 940 | |
| 924 | 941 | ;;; SCALE-FLOAT-MAYBE-OVERFLOW -- Internal
|
| ... | ... | @@ -1135,42 +1152,74 @@ |
| 1135 | 1152 | (assert (= len (the fixnum (1+ digits))))
|
| 1136 | 1153 | (multiple-value-bind (f0)
|
| 1137 | 1154 | (floatit (ash bits -1))
|
| 1138 | - #+nil
|
|
| 1139 | - (progn
|
|
| 1140 | - (format t "x = ~A~%" x)
|
|
| 1141 | - (format t "1: f0, f1 = ~A~%" f0)
|
|
| 1142 | - (format t " scale = ~A~%" (1+ scale)))
|
|
| 1143 | -
|
|
| 1144 | 1155 | (scale-float f0 (1+ scale))))
|
| 1145 | 1156 | (t
|
| 1146 | 1157 | (multiple-value-bind (f0)
|
| 1147 | 1158 | (floatit bits)
|
| 1148 | - #+nil
|
|
| 1149 | - (progn
|
|
| 1150 | - (format t "2: f0, f1 = ~A~%" f0)
|
|
| 1151 | - (format t " scale = ~A~%" scale)
|
|
| 1152 | - (format t "scale-float f0 = ~A~%" (scale-float f0 scale)))
|
|
| 1153 | - (let ((min-exponent
|
|
| 1154 | - ;; Compute the min (unbiased) exponent
|
|
| 1155 | - (ecase format
|
|
| 1156 | - (single-float
|
|
| 1157 | - (- vm:single-float-normal-exponent-min
|
|
| 1158 | - vm:single-float-bias
|
|
| 1159 | - vm:single-float-digits))
|
|
| 1160 | - (double-float
|
|
| 1161 | - (- vm:double-float-normal-exponent-min
|
|
| 1162 | - vm:double-float-bias
|
|
| 1163 | - vm:double-float-digits)))))
|
|
| 1164 | - ;; F0 is always between 0.5 and 1. If
|
|
| 1165 | - ;; SCALE is the min exponent, we have a
|
|
| 1166 | - ;; denormal number just less than the
|
|
| 1167 | - ;; least-positive float. We want to
|
|
| 1168 | - ;; return the least-positive-float so
|
|
| 1169 | - ;; multiply F0 by 2 (without adjusting
|
|
| 1170 | - ;; SCALE) to get the nearest float.
|
|
| 1171 | - (if (= scale min-exponent)
|
|
| 1172 | - (scale-float (* 2 f0) scale)
|
|
| 1173 | - (scale-float f0 scale))))))))
|
|
| 1159 | + (scale-float f0 scale))))))
|
|
| 1160 | + (denormal-excess ()
|
|
| 1161 | + ;; How many bits of precision the result loses by being
|
|
| 1162 | + ;; denormal instead of normal. A normal-precision return
|
|
| 1163 | + ;; would be BITS*2^(SCALE-DIGITS) with BITS having DIGITS
|
|
| 1164 | + ;; bits. Once it's known that this representation will
|
|
| 1165 | + ;; produce a denormal -- equivalently, that SCALE-FLOAT-
|
|
| 1166 | + ;; MAYBE-UNDERFLOW would take the underflow branch --
|
|
| 1167 | + ;; (1 - BIAS) - SCALE bits of the mantissa fall below the
|
|
| 1168 | + ;; denormal's narrower storage and must be rounded off.
|
|
| 1169 | + ;; Zero in the normal range.
|
|
| 1170 | + (let ((bias
|
|
| 1171 | + (ecase format
|
|
| 1172 | + (single-float vm:single-float-bias)
|
|
| 1173 | + (double-float vm:double-float-bias))))
|
|
| 1174 | + (declare (fixnum bias))
|
|
| 1175 | + (max 0 (the fixnum
|
|
| 1176 | + (- (the fixnum (- 1 bias))
|
|
| 1177 | + scale)))))
|
|
| 1178 | + (round-denormal (fraction-and-guard rem excess)
|
|
| 1179 | + ;; FRACTION-AND-GUARD has (1+ DIGITS) bits with one guard
|
|
| 1180 | + ;; bit; round it to (- DIGITS EXCESS) bits using round-to-
|
|
| 1181 | + ;; nearest, ties to even, with REM as the sticky tail.
|
|
| 1182 | + ;; Drops EXCESS+1 low bits in a single step. This is the
|
|
| 1183 | + ;; one rounding the denormal result undergoes; no
|
|
| 1184 | + ;; subsequent SCALE-FLOAT call is needed, so there is no
|
|
| 1185 | + ;; double rounding.
|
|
| 1186 | + (declare (type unsigned-byte fraction-and-guard rem)
|
|
| 1187 | + (fixnum excess))
|
|
| 1188 | + (let* ((shift (1+ excess))
|
|
| 1189 | + (low (ldb (byte shift 0) fraction-and-guard))
|
|
| 1190 | + (quot (ash fraction-and-guard (- shift)))
|
|
| 1191 | + (halfway (ash 1 excess)))
|
|
| 1192 | + (declare (fixnum shift))
|
|
| 1193 | + (cond ((< low halfway) quot)
|
|
| 1194 | + ((> low halfway) (1+ quot))
|
|
| 1195 | + ((not (zerop rem)) (1+ quot))
|
|
| 1196 | + ((oddp quot) (1+ quot))
|
|
| 1197 | + (t quot))))
|
|
| 1198 | + (denormal-from-bits (mantissa excess)
|
|
| 1199 | + ;; MANTISSA has at most (- DIGITS EXCESS) bits and is the
|
|
| 1200 | + ;; stored significand of a denormal result. Denormal
|
|
| 1201 | + ;; storage holds (1- DIGITS) bits, so rounding can carry
|
|
| 1202 | + ;; into the smallest normal only when EXCESS = 1, in
|
|
| 1203 | + ;; which case MANTISSA can be exactly (ASH 1 (1- DIGITS)).
|
|
| 1204 | + (declare (fixnum excess))
|
|
| 1205 | + (let ((sign (if plusp 0 1)))
|
|
| 1206 | + (case format
|
|
| 1207 | + (single-float
|
|
| 1208 | + (cond ((and (= excess 1)
|
|
| 1209 | + (= mantissa
|
|
| 1210 | + (ash 1 (1- vm:single-float-digits))))
|
|
| 1211 | + (single-from-bits
|
|
| 1212 | + sign vm:single-float-normal-exponent-min 0))
|
|
| 1213 | + (t
|
|
| 1214 | + (single-from-bits sign 0 mantissa))))
|
|
| 1215 | + (double-float
|
|
| 1216 | + (cond ((and (= excess 1)
|
|
| 1217 | + (= mantissa
|
|
| 1218 | + (ash 1 (1- vm:double-float-digits))))
|
|
| 1219 | + (double-from-bits
|
|
| 1220 | + sign vm:double-float-normal-exponent-min 0))
|
|
| 1221 | + (t
|
|
| 1222 | + (double-from-bits sign 0 mantissa)))))))
|
|
| 1174 | 1223 | (floatit (bits)
|
| 1175 | 1224 | (let ((sign (if plusp 0 1)))
|
| 1176 | 1225 | (case format
|
| ... | ... | @@ -1188,16 +1237,36 @@ |
| 1188 | 1237 | (declare (fixnum extra))
|
| 1189 | 1238 | (cond ((/= extra 1)
|
| 1190 | 1239 | (assert (> extra 1)))
|
| 1191 | - ((oddp fraction-and-guard)
|
|
| 1192 | - (return
|
|
| 1193 | - (if (zerop rem)
|
|
| 1194 | - (float-and-scale
|
|
| 1195 | - (if (zerop (logand fraction-and-guard 2))
|
|
| 1196 | - fraction-and-guard
|
|
| 1197 | - (1+ fraction-and-guard)))
|
|
| 1198 | - (float-and-scale (1+ fraction-and-guard)))))
|
|
| 1199 | 1240 | (t
|
| 1200 | - (return (float-and-scale fraction-and-guard)))))
|
|
| 1241 | + (return
|
|
| 1242 | + (let ((excess (denormal-excess)))
|
|
| 1243 | + (cond
|
|
| 1244 | + ((zerop excess)
|
|
| 1245 | + ;; Normal result: original odd/even tie-break.
|
|
| 1246 | + (cond ((oddp fraction-and-guard)
|
|
| 1247 | + (if (zerop rem)
|
|
| 1248 | + (float-and-scale
|
|
| 1249 | + (if (zerop
|
|
| 1250 | + (logand fraction-and-guard 2))
|
|
| 1251 | + fraction-and-guard
|
|
| 1252 | + (1+ fraction-and-guard)))
|
|
| 1253 | + (float-and-scale
|
|
| 1254 | + (1+ fraction-and-guard))))
|
|
| 1255 | + (t
|
|
| 1256 | + (float-and-scale fraction-and-guard))))
|
|
| 1257 | + (t
|
|
| 1258 | + ;; Denormal result: re-round directly to the
|
|
| 1259 | + ;; denormal's narrower precision so the only
|
|
| 1260 | + ;; rounding step happens here. Rounding to
|
|
| 1261 | + ;; DIGITS first and re-rounding via
|
|
| 1262 | + ;; SCALE-FLOAT-MAYBE-UNDERFLOW would double-
|
|
| 1263 | + ;; round (e.g. 7.290983e-39 would land on an
|
|
| 1264 | + ;; artifical tie at the 24-bit boundary).
|
|
| 1265 | + (let ((mantissa
|
|
| 1266 | + (round-denormal fraction-and-guard rem
|
|
| 1267 | + excess)))
|
|
| 1268 | + (declare (type unsigned-byte mantissa))
|
|
| 1269 | + (denormal-from-bits mantissa excess)))))))))
|
|
| 1201 | 1270 | (setq shifted-num (ash shifted-num -1))
|
| 1202 | 1271 | (incf scale)))))))
|
| 1203 | 1272 |
| ... | ... | @@ -58,6 +58,8 @@ public domain. |
| 58 | 58 | * #463: `double-double-float` is missing comparison operations
|
| 59 | 59 | between `double-double-float` and `double-float`
|
| 60 | 60 | * #474: Add functions to print and parse C-style hex floats.
|
| 61 | + * #504: Do correct rounding in `scale-float-maybe-underflow`.
|
|
| 62 | + This was causing some denormals to be read incorrectly.
|
|
| 61 | 63 | * Other changes:
|
| 62 | 64 | * Improvements to the PCL implementation of CLOS:
|
| 63 | 65 | * Changes to building procedure:
|
| ... | ... | @@ -179,7 +179,9 @@ |
| 179 | 179 | (kernel::float-ratio-float (* 4 expo) 'double-float))
|
| 180 | 180 | (assert-equal least-positive-double-float
|
| 181 | 181 | (kernel::float-ratio-float (* 494/100 expo) 'double-float))
|
| 182 | - (assert-equal least-positive-double-float
|
|
| 182 | + ;; 988/100*10^-324 is very close to 2*least-positive (the exact ratio
|
|
| 183 | + ;; is 1.9997 * least-positive), so it rounds to 2*least-positive.
|
|
| 184 | + (assert-equal (* 2 least-positive-double-float)
|
|
| 183 | 185 | (kernel::float-ratio-float (* 988/100 expo) 'double-float)))))
|
| 184 | 186 |
|
| 185 | 187 | (define-test reader-error.small-single-floats
|
| ... | ... | @@ -678,8 +680,11 @@ |
| 678 | 680 | (frob cdiv.maxima-case
|
| 679 | 681 | #c(5.43d-10 1.13d-100)
|
| 680 | 682 | #c(1.2d-311 5.7d-312)
|
| 681 | - #c(3.691993880674614517999740937026568563794896024143749539711267954d301
|
|
| 682 | - -1.753697093319947872394996242210428954266103103602859195409591583d301)
|
|
| 683 | + ;; Compute the expected value using rational arithmetic after
|
|
| 684 | + ;; converting the complex numbers above to the equivalent
|
|
| 685 | + ;; complex rationals.
|
|
| 686 | + (/ (complex (rational 5.43d-10) (rational 1.13d-100))
|
|
| 687 | + (complex (rational 1.2d-311) (rational 5.7d-312)))
|
|
| 683 | 688 | 52)
|
| 684 | 689 | ;; 12
|
| 685 | 690 | ;;
|
| ... | ... | @@ -766,3 +771,258 @@ |
| 766 | 771 | (coerce y '(complex single-float)))
|
| 767 | 772 | x
|
| 768 | 773 | y)))
|
| 774 | + |
|
| 775 | +(define-test scale-float-underflow-rounding.single
|
|
| 776 | + (:tag :issues)
|
|
| 777 | + ;; SCALE-FLOAT into the denormal range must round to nearest, ties to
|
|
| 778 | + ;; even, instead of truncating the discarded bits. Each (X EXP BITS)
|
|
| 779 | + ;; triple gives a normal X, an exponent EXP to scale by, and the IEEE
|
|
| 780 | + ;; bits of the expected single-float result.
|
|
| 781 | + (ext:with-float-traps-masked (:underflow :inexact)
|
|
| 782 | + (dolist (case (list
|
|
| 783 | + ;; 1.7f0 * 2^-149: between denormals 1 and 2, closer to 2.
|
|
| 784 | + (list 1.7f0 -149 #x00000002)
|
|
| 785 | + ;; 1.5f0 * 2^-149: halfway between denormals 1 and 2;
|
|
| 786 | + ;; ties round to even -> 2.
|
|
| 787 | + (list 1.5f0 -149 #x00000002)
|
|
| 788 | + ;; 1.1f0 * 2^-149: closer to denormal 1.
|
|
| 789 | + (list 1.1f0 -149 #x00000001)
|
|
| 790 | + ;; 1.0001f0 * 2^-150: just above halfway between 0 and
|
|
| 791 | + ;; smallest denormal; rounds up to 1.
|
|
| 792 | + (list 1.0001f0 -150 #x00000001)
|
|
| 793 | + ;; 1.0f0 * 2^-150: exactly halfway between 0 and smallest
|
|
| 794 | + ;; denormal; ties round to even -> 0.
|
|
| 795 | + (list 1.0f0 -150 #x00000000)
|
|
| 796 | + ;; Largest single < 2 scaled by 2^-127: rounding carries
|
|
| 797 | + ;; into the implicit-1 position and produces the smallest
|
|
| 798 | + ;; normal number.
|
|
| 799 | + (list (kernel:make-single-float #x3fffffff)
|
|
| 800 | + -127 #x00800000)))
|
|
| 801 | + (destructuring-bind (x exp bits) case
|
|
| 802 | + (let ((result (scale-float x exp)))
|
|
| 803 | + (assert-equal bits (kernel:single-float-bits result)
|
|
| 804 | + x exp result))))))
|
|
| 805 | + |
|
| 806 | +(define-test scale-float-underflow-rounding.double
|
|
| 807 | + (:tag :issues)
|
|
| 808 | + ;; Like SCALE-FLOAT-UNDERFLOW-ROUNDING.SINGLE but for double-floats.
|
|
| 809 | + ;; Each (X EXP HI LO) gives a normal X, an exponent EXP, and the IEEE
|
|
| 810 | + ;; high and low bits of the expected double-float result.
|
|
| 811 | + (ext:with-float-traps-masked (:underflow :inexact)
|
|
| 812 | + (dolist (case (list
|
|
| 813 | + ;; 1.7d0 * 2^-1074: between denormals 1 and 2, closer to 2.
|
|
| 814 | + (list 1.7d0 -1074 0 2)
|
|
| 815 | + ;; 1.5d0 * 2^-1074: tie, rounds to even -> 2.
|
|
| 816 | + (list 1.5d0 -1074 0 2)
|
|
| 817 | + ;; 1.1d0 * 2^-1074: closer to denormal 1.
|
|
| 818 | + (list 1.1d0 -1074 0 1)
|
|
| 819 | + ;; 1.0001d0 * 2^-1075: just above halfway, rounds up.
|
|
| 820 | + (list 1.0001d0 -1075 0 1)
|
|
| 821 | + ;; 1.0d0 * 2^-1075: tie at the bottom, rounds to even -> 0.
|
|
| 822 | + (list 1.0d0 -1075 0 0)
|
|
| 823 | + ;; Largest double < 2 scaled by 2^-1023: rounding carries
|
|
| 824 | + ;; into the implicit-1 position and produces the smallest
|
|
| 825 | + ;; normal number.
|
|
| 826 | + (list (kernel:make-double-float #x3fffffff #xffffffff)
|
|
| 827 | + -1023 #x00100000 0)))
|
|
| 828 | + (destructuring-bind (x exp hi lo) case
|
|
| 829 | + (let ((result (scale-float x exp)))
|
|
| 830 | + (assert-equal hi (kernel:double-float-high-bits result)
|
|
| 831 | + x exp result)
|
|
| 832 | + (assert-equal lo (kernel:double-float-low-bits result)
|
|
| 833 | + x exp result))))))
|
|
| 834 | + |
|
| 835 | +(define-test scale-float-underflow-rounding.reader-single
|
|
| 836 | + (:tag :issues)
|
|
| 837 | + ;; The reader uses FLOAT-RATIO-FLOAT, which calls SCALE-FLOAT and
|
|
| 838 | + ;; hence SCALE-FLOAT-MAYBE-UNDERFLOW on denormal results. Reading
|
|
| 839 | + ;; small float literals must therefore also round to nearest.
|
|
| 840 | + (ext:with-float-traps-masked (:underflow :inexact)
|
|
| 841 | + ;; 1.1e-44 is closer to 8 * least-positive-single-float than to 7.
|
|
| 842 | + (assert-equal #x00000008
|
|
| 843 | + (kernel:single-float-bits 1.1e-44))
|
|
| 844 | + ;; 1.121e-44 (essentially the IEEE representation of 1.1e-44) reads
|
|
| 845 | + ;; as the same denormal.
|
|
| 846 | + (assert-equal #x00000008
|
|
| 847 | + (kernel:single-float-bits 1.121e-44))))
|
|
| 848 | + |
|
| 849 | +(define-test scale-float-underflow-rounding.reader-double
|
|
| 850 | + (:tag :issues)
|
|
| 851 | + ;; Like the single-float reader test, in the double denormal range.
|
|
| 852 | + ;; 1.1d-322 is closest to denormal #x16 (= 22).
|
|
| 853 | + (ext:with-float-traps-masked (:underflow :inexact)
|
|
| 854 | + (assert-equal 0 (kernel:double-float-high-bits 1.1d-322))
|
|
| 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))) |