Raymond Toy pushed to branch issue-276-xoroshiro128starstar at cmucl / cmucl
Commits:
b80289c5 by Raymond Toy at 2024-03-13T12:35:32-07:00
Checkout ansi-test branch issue-276-xoroshiro
Use this branch instead of cmucl-expected-failues for running the
tests associated with this merge request.
- - - - -
1 changed file:
- bin/run-ansi-tests.sh
Changes:
=====================================
bin/run-ansi-tests.sh
=====================================
@@ -40,7 +40,7 @@ else
fi
cd ../ansi-test
-git checkout cmucl-expected-failures
+git checkout issue-276-xoroshiro
make LISP="$LISP batch -noinit -nositeinit"
# There should be no unexpected successes or failures; check these separately
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/commit/b80289c54de16cdbfb49ad2…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/commit/b80289c54de16cdbfb49ad2…
You're receiving this email because of your account on gitlab.common-lisp.net.
Raymond Toy pushed to branch issue-276-xoroshiro128starstar at cmucl / cmucl
Commits:
33cf472e by Raymond Toy at 2024-03-08T15:31:41+00:00
Fix #278: Add more debugging prints for gencgc
- - - - -
e16b28cc by Raymond Toy at 2024-03-08T15:31:47+00:00
Merge branch 'issue-278-more-gencgc-debug-prints' into 'master'
Fix #278: Add more debugging prints for gencgc
Closes #278
See merge request cmucl/cmucl!193
- - - - -
9fee7762 by Raymond Toy at 2024-03-08T15:52:35+00:00
Fix #277: return least-positive float for rationals close to it
- - - - -
8faafb32 by Raymond Toy at 2024-03-08T15:52:37+00:00
Merge branch 'issue-277-float-ratio-float-least-positive-float' into 'master'
Fix #277: return least-positive float for rationals close to it
Closes #277
See merge request cmucl/cmucl!191
- - - - -
0d6882c7 by Raymond Toy at 2024-03-10T20:12:37+00:00
Fix #274: 1d999999 hangs cmucl
- - - - -
34f33e19 by Raymond Toy at 2024-03-10T20:12:38+00:00
Merge branch 'issue-274-make-float-of-huge-numbers' into 'master'
Fix #274: 1d999999 hangs cmucl
Closes #274
See merge request cmucl/cmucl!189
- - - - -
a2e2a586 by Raymond Toy at 2024-03-11T23:16:30+00:00
Merge branch 'master' into 'issue-276-xoroshiro128starstar'
# Conflicts:
# src/general-info/release-21f.md
- - - - -
6 changed files:
- src/code/float.lisp
- src/code/reader.lisp
- src/i18n/locale/cmucl.pot
- src/lisp/gencgc.c
- src/lisp/os.h
- tests/float.lisp
Changes:
=====================================
src/code/float.lisp
=====================================
@@ -1137,7 +1137,8 @@
(floatit (ash bits -1))
#+nil
(progn
- (format t "1: f0, f1 = ~A ~A~%" f0 f1)
+ (format t "x = ~A~%" x)
+ (format t "1: f0, f1 = ~A~%" f0)
(format t " scale = ~A~%" (1+ scale)))
(scale-float f0 (1+ scale))))
@@ -1146,14 +1147,30 @@
(floatit bits)
#+nil
(progn
- (format t "2: f0, f1 = ~A ~A~%" f0 f1)
+ (format t "2: f0, f1 = ~A~%" f0)
(format t " scale = ~A~%" scale)
- (format t "scale-float f0 = ~A~%" (scale-float f0 scale))
- (when f1
- (format t "scale-float f1 = ~A~%"
- (scale-float f1 (- scale 53)))))
-
- (scale-float f0 scale))))))
+ (format t "scale-float f0 = ~A~%" (scale-float f0 scale)))
+ (let ((min-exponent
+ ;; Compute the min (unbiased) exponent
+ (ecase format
+ (single-float
+ (- vm:single-float-normal-exponent-min
+ vm:single-float-bias
+ vm:single-float-digits))
+ (double-float
+ (- vm:double-float-normal-exponent-min
+ vm:double-float-bias
+ vm:double-float-digits)))))
+ ;; F0 is always between 0.5 and 1. If
+ ;; SCALE is the min exponent, we have a
+ ;; denormal number just less than the
+ ;; least-positive float. We want to
+ ;; return the least-positive-float so
+ ;; multiply F0 by 2 (without adjusting
+ ;; SCALE) to get the nearest float.
+ (if (= scale min-exponent)
+ (scale-float (* 2 f0) scale)
+ (scale-float f0 scale))))))))
(floatit (bits)
(let ((sign (if plusp 0 1)))
(case format
=====================================
src/code/reader.lisp
=====================================
@@ -1785,7 +1785,7 @@ the end of the stream."
;; Is there an exponent letter?
(cond ((eofp char)
;; If not, we've read the whole number.
- (let ((num (make-float-aux number divisor
+ (let ((num (make-float-aux 0 number divisor
*read-default-float-format*
stream)))
(return-from make-float (if negative-fraction (- num) num))))
@@ -1815,7 +1815,7 @@ the end of the stream."
#+double-double
(#\W 'kernel:double-double-float)))
num)
- (setq num (make-float-aux (* (expt 10 exponent) number) divisor
+ (setq num (make-float-aux exponent number divisor
float-format stream))
(return-from make-float (if negative-fraction
@@ -1823,10 +1823,55 @@ the end of the stream."
num))))
(t (error _"Internal error in floating point reader.")))))
-(defun make-float-aux (number divisor float-format stream)
+(defun make-float-aux (exponent number divisor float-format stream)
+ ;; Computes x = number*10^exponent/divisor.
+ ;;
+ ;; First check to see if x can possibly fit into a float of the
+ ;; given format. So compute log2(x) to get an approximate value of
+ ;; the base 2 exponent of x. If it's too large or too small, we can
+ ;; throw an error immediately. We don't need to be super accurate
+ ;; with the limits. The rest of the code will handle it correctly,
+ ;; even if we're too small or too large.
+ (unless (zerop number)
+ (flet ((fast-log2 (n)
+ ;; For an integer, the integer-length is close enough to
+ ;; the log2 of the number.
+ (integer-length n)))
+ ;; log2(x) = log(number*10^exponent/divisor)
+ ;; = exponent*log2(10) + log2(number)-log2(divisor)
+ (let ((log2-num (+ (* exponent #.(kernel::log2 10d0))
+ (fast-log2 number)
+ (- (fast-log2 divisor)))))
+ (multiple-value-bind (log2-low log2-high)
+ (ecase float-format
+ ((short-float single-float)
+ ;; Single-float exponents range is -149 to 127, but we
+ ;; don't need to be super-accurate since we're
+ ;; multiplying the values by 2.
+ (values (* 2 (- vm:single-float-normal-exponent-min
+ vm:single-float-bias
+ vm:single-float-digits))
+ (* 2 (- vm:single-float-normal-exponent-max
+ vm:single-float-bias))))
+ ((double-float long-float
+ #+double-double kernel:double-double-float)
+ (values (* 2 (- vm:double-float-normal-exponent-min
+ vm:double-float-bias
+ vm:double-float-digits))
+ (* 2 (- vm:double-float-normal-exponent-max
+ vm:double-float-bias)))))
+ ;; Double-float exponent range is -1074 to -1023
+ (unless (< log2-low log2-num log2-high)
+ ;; The number is definitely too large or too small to fit.
+ ;; Signal an error.
+ (%reader-error stream _"Number not representable as a ~S: ~S"
+ float-format (read-buffer-to-string)))))))
+
+ ;; Otherwise the number might fit, so we carefully compute the result
(handler-case
(with-float-traps-masked (:underflow)
- (let* ((ratio (/ number divisor))
+ (let* ((ratio (/ (* (expt 10 exponent) number)
+ divisor))
(result (coerce ratio float-format)))
(when (and (zerop result) (not (zerop number)))
;; The number we've read is so small that it gets
@@ -1850,7 +1895,7 @@ the end of the stream."
result))
(error ()
(%reader-error stream _"Number not representable as a ~S: ~S"
- float-format (/ number divisor)))))
+ float-format (read-buffer-to-string)))))
(defun make-ratio (stream)
=====================================
src/i18n/locale/cmucl.pot
=====================================
@@ -8728,11 +8728,11 @@ msgid "Internal error in floating point reader."
msgstr ""
#: src/code/reader.lisp
-msgid "Underflow"
+msgid "Number not representable as a ~S: ~S"
msgstr ""
#: src/code/reader.lisp
-msgid "Number not representable as a ~S: ~S"
+msgid "Underflow"
msgstr ""
#: src/code/reader.lisp
=====================================
src/lisp/gencgc.c
=====================================
@@ -6756,10 +6756,9 @@ scavenge_newspace_generation_one_scan(int generation)
{
int i;
-#if 0
- fprintf(stderr, "Starting one full scan of newspace generation %d\n",
- generation);
-#endif
+ DPRINTF(gencgc_verbose,
+ (stderr, "Starting one full scan of newspace generation %d\n",
+ generation));
for (i = 0; i < last_free_page; i++) {
if (PAGE_ALLOCATED(i) && !PAGE_UNBOXED(i)
@@ -6864,10 +6863,10 @@ scavenge_newspace_generation_one_scan(int generation)
i = last_page;
}
}
-#if 0
- fprintf(stderr, "Finished one full scan of newspace generation %d\n",
- generation);
-#endif
+
+ DPRINTF(gencgc_verbose,
+ (stderr, "Finished one full scan of newspace generation %d\n",
+ generation));
}
/* Scan all weak objects and reset weak object lists */
@@ -6896,9 +6895,8 @@ scavenge_newspace_generation(int generation)
struct new_area (*previous_new_areas)[] = NULL;
int previous_new_areas_index;
-#if 0
- fprintf(stderr, "Start scavenge_newspace_generation %d\n", generation);
-#endif
+ DPRINTF(gencgc_verbose,
+ (stderr, "Start scavenge_newspace_generation %d\n", generation));
#define SC_NS_GEN_CK 0
#if SC_NS_GEN_CK
@@ -7078,9 +7076,9 @@ scavenge_newspace_generation(int generation)
"*** scav.new.gen. %d: write protected page %d written to? dont_move=%d\n",
generation, i, PAGE_DONT_MOVE(i));
#endif
-#if 0
- fprintf(stderr, "Finished scavenge_newspace_generation %d\n", generation);
-#endif
+
+ DPRINTF(gencgc_verbose,
+ (stderr, "Finished scavenge_newspace_generation %d\n", generation));
}
@@ -7839,20 +7837,26 @@ garbage_collect_generation(int generation, int raise)
scavenge_control_stack();
#endif
+ DPRINTF(gencgc_verbose,
+ (stderr, "Scavenging interrupt handlers ...\n"));
+
scavenge_interrupt_handlers();
-#ifdef PRINTNOISE
- printf("Scavenging the binding stack (%d bytes) ...\n",
- ((lispobj *) get_binding_stack_pointer() -
- binding_stack) * sizeof(lispobj));
-#endif
+ DPRINTF(gencgc_verbose,
+ (stderr, "Done scavenging interrupt handlers\n"));
+
+ DPRINTF(gencgc_verbose,
+ (stderr, "Scavenging the binding stack (%d bytes) ...\n",
+ ((lispobj *) get_binding_stack_pointer() -
+ binding_stack) * sizeof(lispobj)));
+
/* Scavenge the binding stack. */
scavenge(binding_stack,
(lispobj *) get_binding_stack_pointer() - binding_stack);
-#ifdef PRINTNOISE
- printf("Done scavenging the binding stack.\n");
-#endif
+ DPRINTF(gencgc_verbose,
+ (stderr, "Done scavenging the binding stack.\n"));
+
/*
* Scavenge the scavenge_hooks in case this refers to a hook added
* in a prior generation GC. From here on the scavenger_hook will
@@ -7860,30 +7864,44 @@ garbage_collect_generation(int generation, int raise)
* doing here.
*/
-#ifdef PRINTNOISE
- printf("Scavenging the scavenger hooks ...\n");
-#endif
+ DPRINTF(gencgc_verbose,
+ (stderr, "Scavenging the scavenger hooks ...\n"));
+
scavenge(&scavenger_hooks, 1);
-#ifdef PRINTNOISE
- printf("Done scavenging the scavenger hooks.\n");
-#endif
+
+ DPRINTF(gencgc_verbose,
+ (stderr, "Done scavenging the scavenger hooks.\n"));
static_space_size = (lispobj *) SymbolValue(STATIC_SPACE_FREE_POINTER)
- static_space;
- if (gencgc_verbose > 1)
- fprintf(stderr, "Scavenge static space: %ld bytes\n",
- static_space_size * sizeof(lispobj));
+
+ DPRINTF(gencgc_verbose,
+ (stderr, "Scavenge static space: %ld bytes\n",
+ static_space_size * sizeof(lispobj)));
+
scavenge(static_space, static_space_size);
+ DPRINTF(gencgc_verbose,
+ (stderr, "Done scavenging static space\n"));
+
/*
* All generations but the generation being GCed need to be
* scavenged. The new_space generation needs special handling as
* objects may be moved in - it is handle separately below.
*/
- for (i = 0; i < NUM_GENERATIONS; i++)
- if (i != generation && i != new_space)
+ for (i = 0; i < NUM_GENERATIONS; i++) {
+ if (i != generation && i != new_space) {
+ DPRINTF(gencgc_verbose,
+ (stderr, "Scavenge generation %lu (gen = %d, new space = %d)\n",
+ i, generation, new_space));
+
scavenge_generation(i);
+ DPRINTF(gencgc_verbose,
+ (stderr, "Done scavenging generation %lu\n", i));
+ }
+ }
+
/*
* Finally scavenge the new_space generation. Keep going until no
* more objects are moved into the new generation.
=====================================
src/lisp/os.h
=====================================
@@ -8,7 +8,13 @@
#include "lisp.h"
-#define DPRINTF(t,a) { if (t) fprintf a; }
+#if defined(__GNUC__) || defined(__clang__)
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define UNLIKELY(x) (x)
+#endif
+
+#define DPRINTF(t,a) { if (UNLIKELY(t)) fprintf a; }
#ifdef DARWIN
#include "Darwin-os.h"
=====================================
tests/float.lisp
=====================================
@@ -136,5 +136,79 @@
(ext:with-float-traps-masked (:overflow)
(* 100 most-negative-double-float)))))
-
-
\ No newline at end of file
+(define-test float-ratio.single
+ (:tag :issues)
+ ;; least-positive-single-float is 1.4012985e-45. Let's test with
+ ;; some rationals from 7/10*10^-45 to 1.41*10^-45 to make sure they
+ ;; return 0 or least-positive-single-float
+ (let ((expo (expt 10 -45)))
+ ;; Need to make sure underflows are masked.
+ (kernel::with-float-traps-masked (:underflow)
+ ;; 7/10*10^-45 is just under halfway between 0 and least-positive,
+ ;; so the answer is 0.
+ (assert-equal 0f0 (kernel::float-ratio-float (* 7/10 expo) 'single-float))
+
+ ;; These are all more than half way to
+ ;; least-positive-single-float, so they should return that.
+ (assert-equal least-positive-single-float
+ (kernel::float-ratio-float (* 8/10 expo) 'single-float))
+ (assert-equal least-positive-single-float
+ (kernel::float-ratio-float (* 1 expo) 'single-float))
+ (assert-equal least-positive-single-float
+ (kernel::float-ratio-float (* 14/10 expo) 'single-float))
+ (assert-equal least-positive-single-float
+ (kernel::float-ratio-float (* 2 expo) 'single-float)))))
+
+(define-test float-ratio.double
+ (:tag :issues)
+ ;; least-positive-double-float is 4.9406564584124654d-324. Let's
+ ;; test with some rationals from about 2*10^-324 to 4.94*10^-324 to make
+ ;; sure they return 0 or least-positive-double-float
+ (let ((expo (expt 10 -324)))
+ ;; Need to make sure underflows are masked.
+ (kernel::with-float-traps-masked (:underflow)
+ ;; 247/100*10^-324 is just under halfway between 0 and least-positive,
+ ;; so the answer is 0.
+ (assert-equal 0d0 (kernel::float-ratio-float (* 247/100 expo) 'double-float))
+
+ ;; These are all more than half way to
+ ;; least-positive-double-float, so they should return that.
+ (assert-equal least-positive-double-float
+ (kernel::float-ratio-float (* 248/100 expo) 'double-float))
+ (assert-equal least-positive-double-float
+ (kernel::float-ratio-float (* 4 expo) 'double-float))
+ (assert-equal least-positive-double-float
+ (kernel::float-ratio-float (* 494/100 expo) 'double-float))
+ (assert-equal least-positive-double-float
+ (kernel::float-ratio-float (* 988/100 expo) 'double-float)))))
+
+(define-test reader-error.small-single-floats
+ (:tag :issues)
+ ;; Test a number less than half of least-positive-single-float,
+ ;; something a bit smaller, hen then something really small that
+ ;; used to appear to hang cmucl because it was trying to compute the
+ ;; a rational with a huge number of digits.
+ (dolist (num '("1e-46" "1e-80" "1e-999999999"))
+ (assert-error 'reader-error (read-from-string num)
+ num)))
+
+(define-test reader-error.small-double-floats
+ (:tag :issues)
+ ;; Like reader-error.small-single-floats but for doubles
+ (dolist (num '("1d-324" "1d-600" "1d-999999999"))
+ (assert-error 'reader-error (read-from-string num)
+ num)))
+
+(define-test reader-error.big-single-floats
+ (:tag :issues)
+ ;; Signal error for a number just a bit larger than
+ ;; most-positive-single-float. And a really big single-float.
+ (assert-error 'reader-error (read-from-string "3.5e38"))
+ (assert-error 'reader-error (read-from-string "1e999999999")))
+
+(define-test reader-error.big-double-floats
+ (:tag :issues)
+ ;; Signal error for a number just a bit larger than
+ ;; most-positive-double-float. And a really big single-float.
+ (assert-error 'reader-error (read-from-string "1.8d308"))
+ (assert-error 'reader-error (read-from-string "1d999999999")))
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/eb500219950faf934150d7…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/eb500219950faf934150d7…
You're receiving this email because of your account on gitlab.common-lisp.net.
Raymond Toy pushed to branch issue-276-xoroshiro128starstar at cmucl / cmucl
Commits:
7b03d6e1 by Raymond Toy at 2024-03-11T16:05:29-07:00
Remove old xoroshiro jump function; update comments/docstrings
The old xoroshiro jump function is not needed since we now have a Lisp
version of xoroshiro128**; we can use the new jump function everywhere
now.
Update the comments to match the implementation and add some docstrings.
- - - - -
eb500219 by Raymond Toy at 2024-03-11T16:10:39-07:00
Update pot file for new docstrings.
- - - - -
2 changed files:
- src/code/rand-xoroshiro.lisp
- src/i18n/locale/cmucl.pot
Changes:
=====================================
src/code/rand-xoroshiro.lisp
=====================================
@@ -225,18 +225,27 @@
;;;; Random entries:
-;; Sparc and x86 have vops to implement xoroshiro-gen that are much
-;; faster than the portable lisp version. Use them.
-#+(or x86 sparc)
+;; X86 has a vop to implement xoroshiro-gen that is about 4.5 times
+;; faster than the portable lisp version below. For other
+;; architectures, we use the portable version until a vop is written.
+#+x86
(declaim (inline xoroshiro-gen))
-#+(or x86)
+#+x86
(defun xoroshiro-gen (state)
+ _N"Generate the next 64-bit result from the xoroshiro128** generator
+ using the state in STATE, a simple-array of 2 double-floats. The
+ 64-bit result is returned as 2 32-bit values, with the high 32-bits
+ being the first value."
(declare (type (simple-array double-float (2)) state)
(optimize (speed 3) (safety 0)))
(vm::xoroshiro-next state))
-#+(or sparc)
+#-x86
(defun xoroshiro-gen (state)
+ _N"Generate the next 64-bit result from the xoroshiro128** generator
+ using the state in STATE, a simple-array of 2 double-floats. The
+ 64-bit result is returned as 2 32-bit values, with the high 32-bits
+ being the first value."
(declare (type (simple-array double-float (2)) state)
(optimize (speed 3) (safety 0)))
(flet
@@ -502,49 +511,7 @@
:format-arguments (list arg)))))
;; Jump function for the generator. See the jump function in
-;; http://xoroshiro.di.unimi.it/xoroshiro128plus.c
-#-x86
-(defun random-state-jump (&optional (rng-state *random-state*))
- _N"Jump the RNG-STATE. This is equivalent to 2^64 calls to the
- xoroshiro128+ generator. It can be used to generate 2^64
- non-overlapping subsequences for parallel computations."
- (declare (type random-state rng-state))
- (let ((state (random-state-state rng-state))
- (s0-0 0)
- (s0-1 0)
- (s1-0 0)
- (s1-1 0))
- (declare (type (unsigned-byte 32) s0-0 s0-1 s1-0 s1-1)
- (optimize (speed 3) (safety 0)))
- ;; The constants are #xbeac0467eba5facb and #xd86b048b86aa9922,
- ;; and we process these numbers starting from the LSB. We want ot
- ;; process these in 32-bit chunks, so word-reverse the constants.
- (dolist (jump '(#xeba5facb #xbeac0467 #x86aa9922 #xd86b048b))
- (declare (type (unsigned-byte 32) jump))
- (dotimes (b 32)
- (declare (fixnum b))
- (when (logbitp b jump)
- (multiple-value-bind (x1 x0)
- (kernel:double-float-bits (aref state 0))
- (setf s0-1 (logxor s0-1 (ldb (byte 32 0) x1))
- s0-0 (logxor s0-0 x0)))
-
- (multiple-value-bind (x1 x0)
- (kernel:double-float-bits (aref state 1))
- (setf s1-1 (logxor s1-1 (ldb (byte 32 0) x1))
- s1-0 (logxor s1-0 x0))))
- (xoroshiro-gen state)))
-
- (flet ((convert (x1 x0)
- (declare (type (unsigned-byte 32) x1 x0))
- (kernel:make-double-float
- (if (< x1 #x80000000) x1 (- x1 #x100000000))
- x0)))
- (setf (aref state 0) (convert s0-1 s0-0))
- (setf (aref state 1) (convert s1-1 s1-0)))
- rng-state))
-
-#+x86
+;; https://prng.di.unimi.it/xoroshiro128starstar.c
(defun random-state-jump (&optional (rng-state *random-state*))
_N"Jump the RNG-STATE. This is equivalent to 2^64 calls to the
xoroshiro128** generator. It can be used to generate 2^64
=====================================
src/i18n/locale/cmucl.pot
=====================================
@@ -12222,19 +12222,20 @@ msgstr ""
#: src/code/rand-xoroshiro.lisp
msgid ""
-"Generate a uniformly distributed pseudo-random number between zero\n"
-" and Arg. State, if supplied, is the random state to use."
+"Generate the next 64-bit result from the xoroshiro128** generator\n"
+" using the state in STATE, a simple-array of 2 double-floats. The\n"
+" 64-bit result is returned as 2 32-bit values, with the high 32-bits\n"
+" being the first value."
msgstr ""
#: src/code/rand-xoroshiro.lisp
-msgid "Argument is not a positive integer or a positive float: ~S"
+msgid ""
+"Generate a uniformly distributed pseudo-random number between zero\n"
+" and Arg. State, if supplied, is the random state to use."
msgstr ""
#: src/code/rand-xoroshiro.lisp
-msgid ""
-"Jump the RNG-STATE. This is equivalent to 2^64 calls to the\n"
-" xoroshiro128+ generator. It can be used to generate 2^64\n"
-" non-overlapping subsequences for parallel computations."
+msgid "Argument is not a positive integer or a positive float: ~S"
msgstr ""
#: src/code/rand-xoroshiro.lisp
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/d7dbff3925edd162a6bc7d…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/d7dbff3925edd162a6bc7d…
You're receiving this email because of your account on gitlab.common-lisp.net.
Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
0d6882c7 by Raymond Toy at 2024-03-10T20:12:37+00:00
Fix #274: 1d999999 hangs cmucl
- - - - -
34f33e19 by Raymond Toy at 2024-03-10T20:12:38+00:00
Merge branch 'issue-274-make-float-of-huge-numbers' into 'master'
Fix #274: 1d999999 hangs cmucl
Closes #274
See merge request cmucl/cmucl!189
- - - - -
3 changed files:
- src/code/reader.lisp
- src/i18n/locale/cmucl.pot
- tests/float.lisp
Changes:
=====================================
src/code/reader.lisp
=====================================
@@ -1785,7 +1785,7 @@ the end of the stream."
;; Is there an exponent letter?
(cond ((eofp char)
;; If not, we've read the whole number.
- (let ((num (make-float-aux number divisor
+ (let ((num (make-float-aux 0 number divisor
*read-default-float-format*
stream)))
(return-from make-float (if negative-fraction (- num) num))))
@@ -1815,7 +1815,7 @@ the end of the stream."
#+double-double
(#\W 'kernel:double-double-float)))
num)
- (setq num (make-float-aux (* (expt 10 exponent) number) divisor
+ (setq num (make-float-aux exponent number divisor
float-format stream))
(return-from make-float (if negative-fraction
@@ -1823,10 +1823,55 @@ the end of the stream."
num))))
(t (error _"Internal error in floating point reader.")))))
-(defun make-float-aux (number divisor float-format stream)
+(defun make-float-aux (exponent number divisor float-format stream)
+ ;; Computes x = number*10^exponent/divisor.
+ ;;
+ ;; First check to see if x can possibly fit into a float of the
+ ;; given format. So compute log2(x) to get an approximate value of
+ ;; the base 2 exponent of x. If it's too large or too small, we can
+ ;; throw an error immediately. We don't need to be super accurate
+ ;; with the limits. The rest of the code will handle it correctly,
+ ;; even if we're too small or too large.
+ (unless (zerop number)
+ (flet ((fast-log2 (n)
+ ;; For an integer, the integer-length is close enough to
+ ;; the log2 of the number.
+ (integer-length n)))
+ ;; log2(x) = log(number*10^exponent/divisor)
+ ;; = exponent*log2(10) + log2(number)-log2(divisor)
+ (let ((log2-num (+ (* exponent #.(kernel::log2 10d0))
+ (fast-log2 number)
+ (- (fast-log2 divisor)))))
+ (multiple-value-bind (log2-low log2-high)
+ (ecase float-format
+ ((short-float single-float)
+ ;; Single-float exponents range is -149 to 127, but we
+ ;; don't need to be super-accurate since we're
+ ;; multiplying the values by 2.
+ (values (* 2 (- vm:single-float-normal-exponent-min
+ vm:single-float-bias
+ vm:single-float-digits))
+ (* 2 (- vm:single-float-normal-exponent-max
+ vm:single-float-bias))))
+ ((double-float long-float
+ #+double-double kernel:double-double-float)
+ (values (* 2 (- vm:double-float-normal-exponent-min
+ vm:double-float-bias
+ vm:double-float-digits))
+ (* 2 (- vm:double-float-normal-exponent-max
+ vm:double-float-bias)))))
+ ;; Double-float exponent range is -1074 to -1023
+ (unless (< log2-low log2-num log2-high)
+ ;; The number is definitely too large or too small to fit.
+ ;; Signal an error.
+ (%reader-error stream _"Number not representable as a ~S: ~S"
+ float-format (read-buffer-to-string)))))))
+
+ ;; Otherwise the number might fit, so we carefully compute the result
(handler-case
(with-float-traps-masked (:underflow)
- (let* ((ratio (/ number divisor))
+ (let* ((ratio (/ (* (expt 10 exponent) number)
+ divisor))
(result (coerce ratio float-format)))
(when (and (zerop result) (not (zerop number)))
;; The number we've read is so small that it gets
@@ -1850,7 +1895,7 @@ the end of the stream."
result))
(error ()
(%reader-error stream _"Number not representable as a ~S: ~S"
- float-format (/ number divisor)))))
+ float-format (read-buffer-to-string)))))
(defun make-ratio (stream)
=====================================
src/i18n/locale/cmucl.pot
=====================================
@@ -8728,11 +8728,11 @@ msgid "Internal error in floating point reader."
msgstr ""
#: src/code/reader.lisp
-msgid "Underflow"
+msgid "Number not representable as a ~S: ~S"
msgstr ""
#: src/code/reader.lisp
-msgid "Number not representable as a ~S: ~S"
+msgid "Underflow"
msgstr ""
#: src/code/reader.lisp
=====================================
tests/float.lisp
=====================================
@@ -182,3 +182,33 @@
(assert-equal least-positive-double-float
(kernel::float-ratio-float (* 988/100 expo) 'double-float)))))
+(define-test reader-error.small-single-floats
+ (:tag :issues)
+ ;; Test a number less than half of least-positive-single-float,
+ ;; something a bit smaller, hen then something really small that
+ ;; used to appear to hang cmucl because it was trying to compute the
+ ;; a rational with a huge number of digits.
+ (dolist (num '("1e-46" "1e-80" "1e-999999999"))
+ (assert-error 'reader-error (read-from-string num)
+ num)))
+
+(define-test reader-error.small-double-floats
+ (:tag :issues)
+ ;; Like reader-error.small-single-floats but for doubles
+ (dolist (num '("1d-324" "1d-600" "1d-999999999"))
+ (assert-error 'reader-error (read-from-string num)
+ num)))
+
+(define-test reader-error.big-single-floats
+ (:tag :issues)
+ ;; Signal error for a number just a bit larger than
+ ;; most-positive-single-float. And a really big single-float.
+ (assert-error 'reader-error (read-from-string "3.5e38"))
+ (assert-error 'reader-error (read-from-string "1e999999999")))
+
+(define-test reader-error.big-double-floats
+ (:tag :issues)
+ ;; Signal error for a number just a bit larger than
+ ;; most-positive-double-float. And a really big single-float.
+ (assert-error 'reader-error (read-from-string "1.8d308"))
+ (assert-error 'reader-error (read-from-string "1d999999999")))
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/8faafb3259fc5f220c887c…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/8faafb3259fc5f220c887c…
You're receiving this email because of your account on gitlab.common-lisp.net.
Raymond Toy pushed to branch issue-274-make-float-of-huge-numbers at cmucl / cmucl
Commits:
ab7f4ce8 by Raymond Toy at 2024-03-10T12:52:20-07:00
Use builtin constants to get the range of valid exponents.
Instead of hard-wiring the min and max exponents, use
`vm:single-float-normal-expenent-min`, and
`vm:single-float-normal-exponent-max` to get the exponents. These
need to be adjusted by `vm:single-float-bias` and
`vm:single-float-digits` as appropriate to go the actual signed
exponents ranges. We use analogous values for double-floats.
- - - - -
1 changed file:
- src/code/reader.lisp
Changes:
=====================================
src/code/reader.lisp
=====================================
@@ -1845,12 +1845,22 @@ the end of the stream."
(multiple-value-bind (log2-low log2-high)
(ecase float-format
((short-float single-float)
- ;; Single-float exponents range is -149 to 127
- (values (* 2 -149) (* 2 127)))
+ ;; Single-float exponents range is -149 to 127, but we
+ ;; don't need to be super-accurate since we're
+ ;; multiplying the values by 2.
+ (values (* 2 (- vm:single-float-normal-exponent-min
+ vm:single-float-bias
+ vm:single-float-digits))
+ (* 2 (- vm:single-float-normal-exponent-max
+ vm:single-float-bias))))
((double-float long-float
#+double-double kernel:double-double-float)
- ;; Double-float exponent range is -1074 to -1023
- (values (* 2 -1074) (* 2 1023))))
+ (values (* 2 (- vm:double-float-normal-exponent-min
+ vm:double-float-bias
+ vm:double-float-digits))
+ (* 2 (- vm:double-float-normal-exponent-max
+ vm:double-float-bias)))))
+ ;; Double-float exponent range is -1074 to -1023
(unless (< log2-low log2-num log2-high)
;; The number is definitely too large or too small to fit.
;; Signal an error.
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/commit/ab7f4ce8c13a35a79f463d5…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/commit/ab7f4ce8c13a35a79f463d5…
You're receiving this email because of your account on gitlab.common-lisp.net.
Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
9fee7762 by Raymond Toy at 2024-03-08T15:52:35+00:00
Fix #277: return least-positive float for rationals close to it
- - - - -
8faafb32 by Raymond Toy at 2024-03-08T15:52:37+00:00
Merge branch 'issue-277-float-ratio-float-least-positive-float' into 'master'
Fix #277: return least-positive float for rationals close to it
Closes #277
See merge request cmucl/cmucl!191
- - - - -
3 changed files:
- src/code/float.lisp
- src/general-info/release-21f.md
- tests/float.lisp
Changes:
=====================================
src/code/float.lisp
=====================================
@@ -1137,7 +1137,8 @@
(floatit (ash bits -1))
#+nil
(progn
- (format t "1: f0, f1 = ~A ~A~%" f0 f1)
+ (format t "x = ~A~%" x)
+ (format t "1: f0, f1 = ~A~%" f0)
(format t " scale = ~A~%" (1+ scale)))
(scale-float f0 (1+ scale))))
@@ -1146,14 +1147,30 @@
(floatit bits)
#+nil
(progn
- (format t "2: f0, f1 = ~A ~A~%" f0 f1)
+ (format t "2: f0, f1 = ~A~%" f0)
(format t " scale = ~A~%" scale)
- (format t "scale-float f0 = ~A~%" (scale-float f0 scale))
- (when f1
- (format t "scale-float f1 = ~A~%"
- (scale-float f1 (- scale 53)))))
-
- (scale-float f0 scale))))))
+ (format t "scale-float f0 = ~A~%" (scale-float f0 scale)))
+ (let ((min-exponent
+ ;; Compute the min (unbiased) exponent
+ (ecase format
+ (single-float
+ (- vm:single-float-normal-exponent-min
+ vm:single-float-bias
+ vm:single-float-digits))
+ (double-float
+ (- vm:double-float-normal-exponent-min
+ vm:double-float-bias
+ vm:double-float-digits)))))
+ ;; F0 is always between 0.5 and 1. If
+ ;; SCALE is the min exponent, we have a
+ ;; denormal number just less than the
+ ;; least-positive float. We want to
+ ;; return the least-positive-float so
+ ;; multiply F0 by 2 (without adjusting
+ ;; SCALE) to get the nearest float.
+ (if (= scale min-exponent)
+ (scale-float (* 2 f0) scale)
+ (scale-float f0 scale))))))))
(floatit (bits)
(let ((sign (if plusp 0 1)))
(case format
=====================================
src/general-info/release-21f.md
=====================================
@@ -44,6 +44,8 @@ public domain.
* ~~#266~~ Support "~user" in namestrings
* ~~#271~~ Update ASDF to 3.3.7
* ~~#272~~ Move scavenge code for static vectors to its own function
+ * ~~#277~~ `float-ratio-float` returns least postive float for
+ ratios closer to that than zero.
* Other changes:
* Improvements to the PCL implementation of CLOS:
* Changes to building procedure:
=====================================
tests/float.lisp
=====================================
@@ -136,5 +136,49 @@
(ext:with-float-traps-masked (:overflow)
(* 100 most-negative-double-float)))))
-
-
\ No newline at end of file
+(define-test float-ratio.single
+ (:tag :issues)
+ ;; least-positive-single-float is 1.4012985e-45. Let's test with
+ ;; some rationals from 7/10*10^-45 to 1.41*10^-45 to make sure they
+ ;; return 0 or least-positive-single-float
+ (let ((expo (expt 10 -45)))
+ ;; Need to make sure underflows are masked.
+ (kernel::with-float-traps-masked (:underflow)
+ ;; 7/10*10^-45 is just under halfway between 0 and least-positive,
+ ;; so the answer is 0.
+ (assert-equal 0f0 (kernel::float-ratio-float (* 7/10 expo) 'single-float))
+
+ ;; These are all more than half way to
+ ;; least-positive-single-float, so they should return that.
+ (assert-equal least-positive-single-float
+ (kernel::float-ratio-float (* 8/10 expo) 'single-float))
+ (assert-equal least-positive-single-float
+ (kernel::float-ratio-float (* 1 expo) 'single-float))
+ (assert-equal least-positive-single-float
+ (kernel::float-ratio-float (* 14/10 expo) 'single-float))
+ (assert-equal least-positive-single-float
+ (kernel::float-ratio-float (* 2 expo) 'single-float)))))
+
+(define-test float-ratio.double
+ (:tag :issues)
+ ;; least-positive-double-float is 4.9406564584124654d-324. Let's
+ ;; test with some rationals from about 2*10^-324 to 4.94*10^-324 to make
+ ;; sure they return 0 or least-positive-double-float
+ (let ((expo (expt 10 -324)))
+ ;; Need to make sure underflows are masked.
+ (kernel::with-float-traps-masked (:underflow)
+ ;; 247/100*10^-324 is just under halfway between 0 and least-positive,
+ ;; so the answer is 0.
+ (assert-equal 0d0 (kernel::float-ratio-float (* 247/100 expo) 'double-float))
+
+ ;; These are all more than half way to
+ ;; least-positive-double-float, so they should return that.
+ (assert-equal least-positive-double-float
+ (kernel::float-ratio-float (* 248/100 expo) 'double-float))
+ (assert-equal least-positive-double-float
+ (kernel::float-ratio-float (* 4 expo) 'double-float))
+ (assert-equal least-positive-double-float
+ (kernel::float-ratio-float (* 494/100 expo) 'double-float))
+ (assert-equal least-positive-double-float
+ (kernel::float-ratio-float (* 988/100 expo) 'double-float)))))
+
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/e16b28cc4171a3d42265a6…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/e16b28cc4171a3d42265a6…
You're receiving this email because of your account on gitlab.common-lisp.net.