cmucl-cvs
Threads by month
- ----- 2026 -----
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- 1 participants
- 3388 discussions
[Git][cmucl/cmucl][issue-459-more-accurate-dd-complex-div] 3 commits: Fix #456: More accurate complex division
by Raymond Toy (@rtoy) 08 Jan '26
by Raymond Toy (@rtoy) 08 Jan '26
08 Jan '26
Raymond Toy pushed to branch issue-459-more-accurate-dd-complex-div at cmucl / cmucl
Commits:
3416f6d5 by Raymond Toy at 2026-01-08T08:13:59-08:00
Fix #456: More accurate complex division
- - - - -
f8d90cd0 by Raymond Toy at 2026-01-08T08:13:59-08:00
Merge branch 'issue-456-more-accurate-complex-div' into 'master'
Fix #456: More accurate complex division
Closes #456
See merge request cmucl/cmucl!335
- - - - -
118bae2c by Raymond Toy at 2026-01-08T08:22:47-08:00
Merge branch 'master' into issue-459-more-accurate-dd-complex-div
- - - - -
1 changed file:
- tests/float.lisp
Changes:
=====================================
tests/float.lisp
=====================================
@@ -591,3 +591,211 @@
(:tag :issues)
(assert-equal -2w300
(* -2w300 1w0)))
+
+
+
+;; Rudimentary code to read C %a formatted numbers that look like
+;; "-0x1.c4dba4ba1ee79p-620". We assume STRING is exactly in this
+;; format. No error-checking is done.
+(defun parse-hex-float (string)
+ (let* ((sign (if (char= (aref string 0) #\-)
+ -1
+ 1))
+ (dot-posn (position #\. string))
+ (p-posn (position #\p string))
+ (lead (parse-integer string :start (1- dot-posn) :end dot-posn))
+ (frac (parse-integer string :start (1+ dot-posn) :end p-posn :radix 16))
+ (exp (parse-integer string :start (1+ p-posn))))
+ (* sign
+ (scale-float (float (+ (ash lead 52)
+ frac)
+ 1d0)
+ (- exp 52)))))
+
+;; Relative error in terms of bits of accuracy. This is the
+;; definition used by Baudin and Smith. A result of 53 means the two
+;; numbers have identical bits. For complex numbers, we use the min
+;; of the bits of accuracy of the real and imaginary parts.
+(defun rel-err (computed expected)
+ (flet ((rerr (c e)
+ (let ((diff (abs (- c e))))
+ (if (zerop diff)
+ (float-digits diff)
+ (floor (- (log (/ diff (abs e)) 2d0)))))))
+ (min (rerr (realpart computed) (realpart expected))
+ (rerr (imagpart computed) (imagpart expected)))))
+
+(defun do-cdiv-test (x y z-true expected-rel)
+ (let* ((z (/ x y))
+ (rel (rel-err z z-true)))
+ (assert-equal expected-rel
+ rel
+ x y z z-true rel)))
+;; Issue #456: improve accuracy of division of complex double-floats.
+;;
+;; Tests for complex division. Tests 1-10 are from Baudin and Smith.
+;; Test 11 is an example from Maxima. Test 12 is an example from the
+;; ansi-tests where (/ z z) didn't produce exactly 1. Tests 13-16 are
+;; for examples for improvement iterations 1-4 from McGehearty.
+(macrolet
+ ((frob (name x y z-true rel)
+ `(define-test ,name
+ (:tag :issues)
+ (do-cdiv-test ,x ,y ,z-true ,rel))))
+ ;; First cases are from Baudin and Smith
+ ;; 1
+ (frob cdiv.baudin-case.1
+ (complex 1d0 1d0)
+ (complex 1d0 (scale-float 1d0 1023))
+ (complex (scale-float 1d0 -1023)
+ (scale-float -1d0 -1023))
+ 53)
+ ;; 2
+ (frob cdiv.baudin-case.2
+ (complex 1d0 1d0)
+ (complex (scale-float 1d0 -1023) (scale-float 1d0 -1023))
+ (complex (scale-float 1d0 1023) 0)
+ 53)
+ ;; 3
+ (frob cdiv.baudin-case.3
+ (complex (scale-float 1d0 1023) (scale-float 1d0 -1023))
+ (complex (scale-float 1d0 677) (scale-float 1d0 -677))
+ (complex (scale-float 1d0 346) (scale-float -1d0 -1008))
+ 53)
+ ;; 4
+ (frob cdiv.baudin-case.4.overflow
+ (complex (scale-float 1d0 1023) (scale-float 1d0 1023))
+ (complex 1d0 1d0)
+ (complex (scale-float 1d0 1023) 0)
+ 53)
+ ;; 5
+ (frob cdiv.baudin-case.5.underflow-ratio
+ (complex (scale-float 1d0 1020) (scale-float 1d0 -844))
+ (complex (scale-float 1d0 656) (scale-float 1d0 -780))
+ (complex (scale-float 1d0 364) (scale-float -1d0 -1072))
+ 53)
+ ;; 6
+ (frob cdiv.baudin-case.6.underflow-realpart
+ (complex (scale-float 1d0 -71) (scale-float 1d0 1021))
+ (complex (scale-float 1d0 1001) (scale-float 1d0 -323))
+ (complex (scale-float 1d0 -1072) (scale-float 1d0 20))
+ 53)
+ ;; 7
+ (frob cdiv.baudin-case.7.overflow-both-parts
+ (complex (scale-float 1d0 -347) (scale-float 1d0 -54))
+ (complex (scale-float 1d0 -1037) (scale-float 1d0 -1058))
+ (complex 3.898125604559113300d289 8.174961907852353577d295)
+ 53)
+ ;; 8
+ (frob cdiv.baudin-case.8
+ (complex (scale-float 1d0 -1074) (scale-float 1d0 -1074))
+ (complex (scale-float 1d0 -1073) (scale-float 1d0 -1074))
+ (complex 0.6d0 0.2d0)
+ 53)
+ ;; 9
+ (frob cdiv.baudin-case.9
+ (complex (scale-float 1d0 1015) (scale-float 1d0 -989))
+ (complex (scale-float 1d0 1023) (scale-float 1d0 1023))
+ (complex 0.001953125d0 -0.001953125d0)
+ 53)
+ ;; 10
+ (frob cdiv.baudin-case.10.improve-imagpart-accuracy
+ (complex (scale-float 1d0 -622) (scale-float 1d0 -1071))
+ (complex (scale-float 1d0 -343) (scale-float 1d0 -798))
+ (complex 1.02951151789360578d-84 6.97145987515076231d-220)
+ 53)
+ ;; 11
+ ;;
+ ;; From Maxima. This was from a (private) email where Maxima used
+ ;; CL:/ to compute the ratio but was not very accurate.
+ (frob cdiv.maxima-case
+ #c(5.43d-10 1.13d-100)
+ #c(1.2d-311 5.7d-312)
+ #c(3.691993880674614517999740937026568563794896024143749539711267954d301
+ -1.753697093319947872394996242210428954266103103602859195409591583d301)
+ 52)
+ ;; 12
+ ;;
+ ;; Found by ansi tests. z/z should be exactly 1.
+ (frob cdiv.ansi-test-z/z
+ #c(1.565640716292489d19 0.0d0)
+ #c(1.565640716292489d19 0.0d0)
+ #c(1d0 0)
+ 53)
+ ;; 13
+ ;; Iteration 1. Without this, we would instead return
+ ;;
+ ;; (complex (parse-hex-float "0x1.ba8df8075bceep+155")
+ ;; (parse-hex-float "-0x1.a4ad6329485f0p-895"))
+ ;;
+ ;; whose imaginary part is quite a bit off.
+ (frob cdiv.mcgehearty-iteration.1
+ (complex (parse-hex-float "0x1.73a3dac1d2f1fp+509")
+ (parse-hex-float "-0x1.c4dba4ba1ee79p-620"))
+ (complex (parse-hex-float "0x1.adf526c249cf0p+353")
+ (parse-hex-float "0x1.98b3fbc1677bbp-697"))
+ (complex (parse-hex-float "0x1.BA8DF8075BCEEp+155")
+ (parse-hex-float "-0x1.A4AD628DA5B74p-895"))
+ 53)
+ ;; 14
+ ;; Iteration 2.
+ (frob cdiv.mcgehearty-iteration.2
+ (complex (parse-hex-float "-0x0.000000008e4f8p-1022")
+ (parse-hex-float "0x0.0000060366ba7p-1022"))
+ (complex (parse-hex-float "-0x1.605b467369526p-245")
+ (parse-hex-float "0x1.417bd33105808p-256"))
+ (complex (parse-hex-float "0x1.cde593daa4ffep-810")
+ (parse-hex-float "-0x1.179b9a63df6d3p-799"))
+ 52)
+ ;; 15
+ ;; Iteration 3
+ (frob cdiv.mcgehearty-iteration.3
+ (complex (parse-hex-float "0x1.cb27eece7c585p-355 ")
+ (parse-hex-float "0x0.000000223b8a8p-1022"))
+ (complex (parse-hex-float "-0x1.74e7ed2b9189fp-22")
+ (parse-hex-float "0x1.3d80439e9a119p-731"))
+ (complex (parse-hex-float "-0x1.3b35ed806ae5ap-333")
+ (parse-hex-float "-0x0.05e01bcbfd9f6p-1022"))
+ 53)
+ ;; 16
+ ;; Iteration 4
+ (frob cdiv.mcgehearty-iteration.4
+ (complex (parse-hex-float "-0x1.f5c75c69829f0p-530")
+ (parse-hex-float "-0x1.e73b1fde6b909p+316"))
+ (complex (parse-hex-float "-0x1.ff96c3957742bp+1023")
+ (parse-hex-float "0x1.5bd78c9335899p+1021"))
+ (complex (parse-hex-float "-0x1.423c6ce00c73bp-710")
+ (parse-hex-float "0x1.d9edcf45bcb0ep-708"))
+ 52))
+
+(define-test complex-division.misc
+ (:tag :issue)
+ (let ((num '(1
+ 1/2
+ 1.0
+ 1d0
+ #c(1 2)
+ #c(1.0 2.0)
+ #c(1d0 2d0)
+ #c(1w0 2w0))))
+ ;; Try all combinations of divisions of different types. This is
+ ;; primarily to test that we got all the numeric contagion cases
+ ;; for division in CL:/.
+ (dolist (x num)
+ (dolist (y num)
+ (assert-true (/ x y)
+ x y)))))
+
+(define-test complex-division.single
+ (:tag :issues)
+ (let* ((x #c(1 2))
+ (y (complex (expt 2 127) (expt 2 127)))
+ (expected (coerce (/ x y)
+ '(complex single-float))))
+ ;; A naive implementation of complex division would cause an
+ ;; overflow in computing the denominator.
+ (assert-equal expected
+ (/ (coerce x '(complex single-float))
+ (coerce y '(complex single-float)))
+ x
+ y)))
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/78e7201838cc9b7f0de878…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/78e7201838cc9b7f0de878…
You're receiving this email because of your account on gitlab.common-lisp.net.
1
0
[Git][cmucl/cmucl][master] 2 commits: Fix #456: More accurate complex division
by Raymond Toy (@rtoy) 08 Jan '26
by Raymond Toy (@rtoy) 08 Jan '26
08 Jan '26
Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
3416f6d5 by Raymond Toy at 2026-01-08T08:13:59-08:00
Fix #456: More accurate complex division
- - - - -
f8d90cd0 by Raymond Toy at 2026-01-08T08:13:59-08:00
Merge branch 'issue-456-more-accurate-complex-div' into 'master'
Fix #456: More accurate complex division
Closes #456
See merge request cmucl/cmucl!335
- - - - -
4 changed files:
- src/code/numbers.lisp
- src/compiler/float-tran.lisp
- src/general-info/release-22a.md
- tests/float.lisp
Changes:
=====================================
src/code/numbers.lisp
=====================================
@@ -593,26 +593,250 @@
(build-ratio x y)
(build-ratio (truncate x gcd) (truncate y gcd))))))
+;; An implementation of Baudin and Smith's robust complex division for
+;; double-floats. This is a pretty straightforward translation of the
+;; original in https://arxiv.org/pdf/1210.4539.
+;;
+;; This also includes improvements mentioned in
+;; https://lpc.events/event/11/contributions/1005/attachments/856/1625/Complex….
+;; In particular iteration 1 and 3 are added. Iteration 2 and 4 were
+;; not added. The test examples from iteration 2 and 4 didn't change
+;; with or without changes added.
+(defconstant +cdiv-rmin+ least-positive-normalized-double-float)
+(defconstant +cdiv-rbig+ (/ most-positive-double-float 2))
+(defconstant +cdiv-rmin2+ (scale-float 1d0 -53))
+(defconstant +cdiv-rminscal+ (scale-float 1d0 51))
+(defconstant +cdiv-rmax2+ (* +cdiv-rbig+ +cdiv-rmin2+))
+;; This is the value of %eps from Scilab
+(defconstant +cdiv-eps+ (scale-float 1d0 -52))
+(defconstant +cdiv-be+ (/ 2 (* +cdiv-eps+ +cdiv-eps+)))
+(defconstant +cdiv-2/eps+ (/ 2 +cdiv-eps+))
+
+;; Make these functions accessible. cdiv-double-float and
+;; cdiv-single-float are used by deftransforms. Of course, two-arg-/
+;; is the interface to division. cdiv-generic isn't used anywhere
+;; else.
+(declaim (ext:start-block cdiv-double-float cdiv-single-float two-arg-/))
+
+(defun cdiv-double-float (x y)
+ "Accurate division of complex double-float numbers x and y."
+ (declare (type (complex double-float) x y)
+ (optimize (speed 3) (safety 0)))
+ (labels
+ ((internal-compreal (a b c d r tt)
+ (declare (double-float a b c d r tt))
+ ;; Compute the real part of the complex division
+ ;; (a+ib)/(c+id), assuming |c| <= |d|. r = d/c and tt = 1/(c+d*r).
+ ;;
+ ;; The realpart is (a*c+b*d)/(c^2+d^2).
+ ;;
+ ;; c^2+d^2 = c*(c+d*(d/c)) = c*(c+d*r)
+ ;;
+ ;; Then
+ ;;
+ ;; (a*c+b*d)/(c^2+d^2) = (a*c+b*d)/(c*(c+d*r))
+ ;; = (a + b*d/c)/(c+d*r)
+ ;; = (a + b*r)/(c + d*r).
+ ;;
+ ;; Thus tt = (c + d*r).
+ (cond ((>= (abs r) +cdiv-rmin+)
+ (let ((br (* b r)))
+ (if (/= br 0)
+ (/ (+ a br) tt)
+ ;; b*r underflows. Instead, compute
+ ;;
+ ;; (a + b*r)*tt = a*tt + b*tt*r
+ ;; = a*tt + (b*tt)*r
+ ;; (a + b*r)/tt = a/tt + b/tt*r
+ ;; = a*tt + (b*tt)*r
+ (+ (/ a tt)
+ (* (/ b tt)
+ r)))))
+ (t
+ ;; r = 0 so d is very tiny compared to c.
+ ;;
+ (/ (+ a (* d (/ b c)))
+ tt))))
+ (robust-subinternal (a b c d)
+ (declare (double-float a b c d))
+ (let* ((r (/ d c))
+ (tt (+ c (* d r))))
+ ;; e is the real part and f is the imaginary part. We
+ ;; can use internal-compreal for the imaginary part by
+ ;; noticing that the imaginary part of (a+i*b)/(c+i*d) is
+ ;; the same as the real part of (b-i*a)/(c+i*d).
+ (let ((e (internal-compreal a b c d r tt))
+ (f (internal-compreal b (- a) c d r tt)))
+ (values e
+ f))))
+
+ (robust-internal (x y)
+ (declare (type (complex double-float) x y))
+ (let ((a (realpart x))
+ (b (imagpart x))
+ (c (realpart y))
+ (d (imagpart y)))
+ (declare (double-float a b c d))
+ (flet ((maybe-scale (abs-tst a b c d)
+ (declare (double-float a b c d))
+ ;; This implements McGehearty's iteration 3 to
+ ;; handle the case when some values are too big
+ ;; and should be scaled down. Also if some
+ ;; values are too tiny, scale them up.
+ (let ((abs-a (abs a))
+ (abs-b (abs b)))
+ (if (or (> abs-tst +cdiv-rbig+)
+ (> abs-a +cdiv-rbig+)
+ (> abs-b +cdiv-rbig+))
+ (setf a (* a 0.5d0)
+ b (* b 0.5d0)
+ c (* c 0.5d0)
+ d (* d 0.5d0))
+ (if (< abs-tst +cdiv-rmin2+)
+ (setf a (* a +cdiv-rminscal+)
+ b (* b +cdiv-rminscal+)
+ c (* c +cdiv-rminscal+)
+ d (* d +cdiv-rminscal+))
+ (if (or (and (< abs-a +cdiv-rmin+)
+ (< abs-b +cdiv-rmax2+)
+ (< abs-tst +cdiv-rmax2+))
+ (and (< abs-b +cdiv-rmin+)
+ (< abs-a +cdiv-rmax2+)
+ (< abs-tst +cdiv-rmax2+)))
+ (setf a (* a +cdiv-rminscal+)
+ b (* b +cdiv-rminscal+)
+ c (* c +cdiv-rminscal+)
+ d (* d +cdiv-rminscal+)))))
+ (values a b c d))))
+ (cond
+ ((<= (abs d) (abs c))
+ ;; |d| <= |c|, so we can use robust-subinternal to
+ ;; perform the division.
+ (multiple-value-bind (a b c d)
+ (maybe-scale (abs c) a b c d)
+ (multiple-value-bind (e f)
+ (robust-subinternal a b c d)
+ (complex e f))))
+ (t
+ ;; |d| > |c|. So, instead compute
+ ;;
+ ;; (b + i*a)/(d + i*c) = ((b*d+a*c) + (a*d-b*c)*i)/(d^2+c^2)
+ ;;
+ ;; Compare this to (a+i*b)/(c+i*d) and we see that
+ ;; realpart of the former is the same, but the
+ ;; imagpart of the former is the negative of the
+ ;; desired division.
+ (multiple-value-bind (a b c d)
+ (maybe-scale (abs d) a b c d)
+ (multiple-value-bind (e f)
+ (robust-subinternal b a d c)
+ (complex e (- f))))))))))
+ (let* ((a (realpart x))
+ (b (imagpart x))
+ (c (realpart y))
+ (d (imagpart y))
+ (ab (max (abs a) (abs b)))
+ (cd (max (abs c) (abs d)))
+ (s 1d0))
+ (declare (double-float s))
+ ;; If a or b is big, scale down a and b.
+ (when (>= ab +cdiv-rbig+)
+ (setf x (/ x 2)
+ s (* s 2)))
+ ;; If c or d is big, scale down c and d.
+ (when (>= cd +cdiv-rbig+)
+ (setf y (/ y 2)
+ s (/ s 2)))
+ ;; If a or b is tiny, scale up a and b.
+ (when (<= ab (* +cdiv-rmin+ +cdiv-2/eps+))
+ (setf x (* x +cdiv-be+)
+ s (/ s +cdiv-be+)))
+ ;; If c or d is tiny, scale up c and d.
+ (when (<= cd (* +cdiv-rmin+ +cdiv-2/eps+))
+ (setf y (* y +cdiv-be+)
+ s (* s +cdiv-be+)))
+ (* s
+ (robust-internal x y)))))
+
+;; Smith's algorithm for complex division for (complex single-float).
+;; We convert the parts to double-floats before computing the result.
+(defun cdiv-single-float (x y)
+ "Accurate division of complex single-float numbers x and y."
+ (declare (type (complex single-float) x y))
+ (let ((a (float (realpart x) 1d0))
+ (b (float (imagpart x) 1d0))
+ (c (float (realpart y) 1d0))
+ (d (float (imagpart y) 1d0)))
+ (cond ((< (abs c) (abs d))
+ (let* ((r (/ c d))
+ (denom (+ (* c r) d))
+ (e (float (/ (+ (* a r) b) denom) 1f0))
+ (f (float (/ (- (* b r) a) denom) 1f0)))
+ (complex e f)))
+ (t
+ (let* ((r (/ d c))
+ (denom (+ c (* d r)))
+ (e (float (/ (+ a (* b r)) denom) 1f0))
+ (f (float (/ (- b (* a r)) denom) 1f0)))
+ (complex e f))))))
+
+;; Generic implementation of Smith's algorithm.
+(defun cdiv-generic (x y)
+ "Complex division of generic numbers x and y. One of x or y should be
+ a complex."
+ (let ((a (realpart x))
+ (b (imagpart x))
+ (c (realpart y))
+ (d (imagpart y)))
+ (cond ((< (abs c) (abs d))
+ (let* ((r (/ c d))
+ (denom (+ (* c r) d))
+ (e (/ (+ (* a r) b) denom))
+ (f (/ (- (* b r) a) denom)))
+ (canonical-complex e f)))
+ (t
+ (let* ((r (/ d c))
+ (denom (+ c (* d r)))
+ (e (/ (+ a (* b r)) denom))
+ (f (/ (- b (* a r)) denom)))
+ (canonical-complex e f))))))
(defun two-arg-/ (x y)
(number-dispatch ((x number) (y number))
(float-contagion / x y (ratio integer))
-
- ((complex complex)
- (let* ((rx (realpart x))
- (ix (imagpart x))
- (ry (realpart y))
- (iy (imagpart y)))
- (if (> (abs ry) (abs iy))
- (let* ((r (/ iy ry))
- (dn (+ ry (* r iy))))
- (canonical-complex (/ (+ rx (* ix r)) dn)
- (/ (- ix (* rx r)) dn)))
- (let* ((r (/ ry iy))
- (dn (+ iy (* r ry))))
- (canonical-complex (/ (+ (* rx r) ix) dn)
- (/ (- (* ix r) rx) dn))))))
- (((foreach integer ratio single-float double-float) complex)
+
+ (((complex single-float)
+ (foreach (complex rational) (complex single-float)))
+ (cdiv-single-float x (coerce y '(complex single-float))))
+ (((complex double-float)
+ (foreach (complex rational) (complex single-float) (complex double-float)))
+ (cdiv-double-float x (coerce y '(complex double-float))))
+
+ (((foreach integer ratio single-float (complex rational))
+ (complex single-float))
+ (cdiv-single-float (coerce x '(complex single-float))
+ y))
+
+ (((foreach integer ratio single-float double-float (complex rational)
+ (complex single-float))
+ (complex double-float))
+ (cdiv-double-float (coerce x '(complex double-float))
+ y))
+ (((complex double-double-float)
+ (foreach (complex rational) (complex single-float) (complex double-float)
+ (complex double-double-float)))
+ ;; We should do something better for double-double floats.
+ (cdiv-generic x y))
+
+ (((foreach integer ratio single-float double-float double-double-float
+ (complex rational) (complex single-float) (complex double-float))
+ (complex double-double-float))
+ (cdiv-generic x y))
+
+ (((foreach integer ratio single-float double-float double-double-float)
+ (complex rational))
+ ;; Smith's algorithm, but takes advantage of the fact that the
+ ;; numerator is a real number and not complex.
(let* ((ry (realpart y))
(iy (imagpart y)))
(if (> (abs ry) (abs iy))
@@ -624,10 +848,23 @@
(dn (* iy (+ 1 (* r r)))))
(canonical-complex (/ (* x r) dn)
(/ (- x) dn))))))
- ((complex (or rational float))
+ (((complex rational)
+ (complex rational))
+ ;; We probably don't need to do Smith's algorithm for rationals.
+ ;; A naive implementation of coplex division has no issues.
+ (cdiv-generic x y))
+
+ (((foreach (complex rational) (complex single-float) (complex double-float)
+ (complex double-double-float))
+ (or rational float))
(canonical-complex (/ (realpart x) y)
(/ (imagpart x) y)))
-
+
+ ((double-float
+ (complex single-float))
+ (cdiv-double-float (coerce x '(complex double-float))
+ (coerce y '(complex double-float))))
+
((ratio ratio)
(let* ((nx (numerator x))
(dx (denominator x))
@@ -656,6 +893,7 @@
(build-ratio (maybe-truncate nx gcd)
(* (maybe-truncate y gcd) (denominator x)))))))
+(declaim (ext:end-block))
(defun %negate (n)
(number-dispatch ((n number))
=====================================
src/compiler/float-tran.lisp
=====================================
@@ -1804,46 +1804,13 @@
#+double-double
(frob double-double-float))
-#+(and sse2 complex-fp-vops)
(macrolet
- ((frob (type one)
- `(deftransform / ((x y) (,type ,type) *
- :policy (> speed space))
- ;; Divide a complex by a complex
+ ((frob (type name)
+ `(deftransform / ((x y) ((complex ,type) (complex ,type)) *)
+ (,name x y))))
+ (frob double-float kernel::cdiv-double-float)
+ (frob single-float kernel::cdiv-single-float))
- ;; Here's how we do a complex division
- ;;
- ;; Compute (xr + i*xi)/(yr + i*yi)
- ;;
- ;; Assume |yi| < |yr|. Then
- ;;
- ;; (xr + i*xi) (xr + i*xi)
- ;; ----------- = -----------------
- ;; (yr + i*yi) yr*(1 + i*(yi/yr))
- ;;
- ;; (xr + i*xi)*(1 - i*(yi/yr))
- ;; = ---------------------------
- ;; yr*(1 + (yi/yr)^2)
- ;;
- ;; (xr + i*xi)*(1 - i*(yi/yr))
- ;; = ---------------------------
- ;; yr + (yi/yr)*yi
- ;;
- ;; This allows us to use a fast complex multiply followed by
- ;; a real division.
- '(let* ((ry (realpart y))
- (iy (imagpart y)))
- (if (> (abs ry) (abs iy))
- (let* ((r (/ iy ry))
- (dn (+ ry (* r iy))))
- (/ (* x (complex ,one r))
- dn))
- (let* ((r (/ ry iy))
- (dn (+ iy (* r ry))))
- (/ (* x (complex r ,(- one)))
- dn)))))))
- (frob (complex single-float) 1f0)
- (frob (complex double-float) 1d0))
;;;; Complex contagion:
=====================================
src/general-info/release-22a.md
=====================================
@@ -34,6 +34,9 @@ public domain.
* #446: Use C compiler to get errno values to update UNIX
defpackage with errno symbols
* #453: Use correct flags for analyzer and always save logs.
+ * #456: Improve accuracy for division of complex double-floats
+ using Baudin and Smith's robust complex division algorithm
+ with improvements by Patrick McGehearty.
* #458: Spurious overflow in double-double-float multiply
* Other changes:
* Improvements to the PCL implementation of CLOS:
=====================================
tests/float.lisp
=====================================
@@ -348,3 +348,211 @@
(:tag :issues)
(assert-equal -2w300
(* -2w300 1w0)))
+
+
+
+;; Rudimentary code to read C %a formatted numbers that look like
+;; "-0x1.c4dba4ba1ee79p-620". We assume STRING is exactly in this
+;; format. No error-checking is done.
+(defun parse-hex-float (string)
+ (let* ((sign (if (char= (aref string 0) #\-)
+ -1
+ 1))
+ (dot-posn (position #\. string))
+ (p-posn (position #\p string))
+ (lead (parse-integer string :start (1- dot-posn) :end dot-posn))
+ (frac (parse-integer string :start (1+ dot-posn) :end p-posn :radix 16))
+ (exp (parse-integer string :start (1+ p-posn))))
+ (* sign
+ (scale-float (float (+ (ash lead 52)
+ frac)
+ 1d0)
+ (- exp 52)))))
+
+;; Relative error in terms of bits of accuracy. This is the
+;; definition used by Baudin and Smith. A result of 53 means the two
+;; numbers have identical bits. For complex numbers, we use the min
+;; of the bits of accuracy of the real and imaginary parts.
+(defun rel-err (computed expected)
+ (flet ((rerr (c e)
+ (let ((diff (abs (- c e))))
+ (if (zerop diff)
+ (float-digits diff)
+ (floor (- (log (/ diff (abs e)) 2d0)))))))
+ (min (rerr (realpart computed) (realpart expected))
+ (rerr (imagpart computed) (imagpart expected)))))
+
+(defun do-cdiv-test (x y z-true expected-rel)
+ (let* ((z (/ x y))
+ (rel (rel-err z z-true)))
+ (assert-equal expected-rel
+ rel
+ x y z z-true rel)))
+;; Issue #456: improve accuracy of division of complex double-floats.
+;;
+;; Tests for complex division. Tests 1-10 are from Baudin and Smith.
+;; Test 11 is an example from Maxima. Test 12 is an example from the
+;; ansi-tests where (/ z z) didn't produce exactly 1. Tests 13-16 are
+;; for examples for improvement iterations 1-4 from McGehearty.
+(macrolet
+ ((frob (name x y z-true rel)
+ `(define-test ,name
+ (:tag :issues)
+ (do-cdiv-test ,x ,y ,z-true ,rel))))
+ ;; First cases are from Baudin and Smith
+ ;; 1
+ (frob cdiv.baudin-case.1
+ (complex 1d0 1d0)
+ (complex 1d0 (scale-float 1d0 1023))
+ (complex (scale-float 1d0 -1023)
+ (scale-float -1d0 -1023))
+ 53)
+ ;; 2
+ (frob cdiv.baudin-case.2
+ (complex 1d0 1d0)
+ (complex (scale-float 1d0 -1023) (scale-float 1d0 -1023))
+ (complex (scale-float 1d0 1023) 0)
+ 53)
+ ;; 3
+ (frob cdiv.baudin-case.3
+ (complex (scale-float 1d0 1023) (scale-float 1d0 -1023))
+ (complex (scale-float 1d0 677) (scale-float 1d0 -677))
+ (complex (scale-float 1d0 346) (scale-float -1d0 -1008))
+ 53)
+ ;; 4
+ (frob cdiv.baudin-case.4.overflow
+ (complex (scale-float 1d0 1023) (scale-float 1d0 1023))
+ (complex 1d0 1d0)
+ (complex (scale-float 1d0 1023) 0)
+ 53)
+ ;; 5
+ (frob cdiv.baudin-case.5.underflow-ratio
+ (complex (scale-float 1d0 1020) (scale-float 1d0 -844))
+ (complex (scale-float 1d0 656) (scale-float 1d0 -780))
+ (complex (scale-float 1d0 364) (scale-float -1d0 -1072))
+ 53)
+ ;; 6
+ (frob cdiv.baudin-case.6.underflow-realpart
+ (complex (scale-float 1d0 -71) (scale-float 1d0 1021))
+ (complex (scale-float 1d0 1001) (scale-float 1d0 -323))
+ (complex (scale-float 1d0 -1072) (scale-float 1d0 20))
+ 53)
+ ;; 7
+ (frob cdiv.baudin-case.7.overflow-both-parts
+ (complex (scale-float 1d0 -347) (scale-float 1d0 -54))
+ (complex (scale-float 1d0 -1037) (scale-float 1d0 -1058))
+ (complex 3.898125604559113300d289 8.174961907852353577d295)
+ 53)
+ ;; 8
+ (frob cdiv.baudin-case.8
+ (complex (scale-float 1d0 -1074) (scale-float 1d0 -1074))
+ (complex (scale-float 1d0 -1073) (scale-float 1d0 -1074))
+ (complex 0.6d0 0.2d0)
+ 53)
+ ;; 9
+ (frob cdiv.baudin-case.9
+ (complex (scale-float 1d0 1015) (scale-float 1d0 -989))
+ (complex (scale-float 1d0 1023) (scale-float 1d0 1023))
+ (complex 0.001953125d0 -0.001953125d0)
+ 53)
+ ;; 10
+ (frob cdiv.baudin-case.10.improve-imagpart-accuracy
+ (complex (scale-float 1d0 -622) (scale-float 1d0 -1071))
+ (complex (scale-float 1d0 -343) (scale-float 1d0 -798))
+ (complex 1.02951151789360578d-84 6.97145987515076231d-220)
+ 53)
+ ;; 11
+ ;;
+ ;; From Maxima. This was from a (private) email where Maxima used
+ ;; CL:/ to compute the ratio but was not very accurate.
+ (frob cdiv.maxima-case
+ #c(5.43d-10 1.13d-100)
+ #c(1.2d-311 5.7d-312)
+ #c(3.691993880674614517999740937026568563794896024143749539711267954d301
+ -1.753697093319947872394996242210428954266103103602859195409591583d301)
+ 52)
+ ;; 12
+ ;;
+ ;; Found by ansi tests. z/z should be exactly 1.
+ (frob cdiv.ansi-test-z/z
+ #c(1.565640716292489d19 0.0d0)
+ #c(1.565640716292489d19 0.0d0)
+ #c(1d0 0)
+ 53)
+ ;; 13
+ ;; Iteration 1. Without this, we would instead return
+ ;;
+ ;; (complex (parse-hex-float "0x1.ba8df8075bceep+155")
+ ;; (parse-hex-float "-0x1.a4ad6329485f0p-895"))
+ ;;
+ ;; whose imaginary part is quite a bit off.
+ (frob cdiv.mcgehearty-iteration.1
+ (complex (parse-hex-float "0x1.73a3dac1d2f1fp+509")
+ (parse-hex-float "-0x1.c4dba4ba1ee79p-620"))
+ (complex (parse-hex-float "0x1.adf526c249cf0p+353")
+ (parse-hex-float "0x1.98b3fbc1677bbp-697"))
+ (complex (parse-hex-float "0x1.BA8DF8075BCEEp+155")
+ (parse-hex-float "-0x1.A4AD628DA5B74p-895"))
+ 53)
+ ;; 14
+ ;; Iteration 2.
+ (frob cdiv.mcgehearty-iteration.2
+ (complex (parse-hex-float "-0x0.000000008e4f8p-1022")
+ (parse-hex-float "0x0.0000060366ba7p-1022"))
+ (complex (parse-hex-float "-0x1.605b467369526p-245")
+ (parse-hex-float "0x1.417bd33105808p-256"))
+ (complex (parse-hex-float "0x1.cde593daa4ffep-810")
+ (parse-hex-float "-0x1.179b9a63df6d3p-799"))
+ 52)
+ ;; 15
+ ;; Iteration 3
+ (frob cdiv.mcgehearty-iteration.3
+ (complex (parse-hex-float "0x1.cb27eece7c585p-355 ")
+ (parse-hex-float "0x0.000000223b8a8p-1022"))
+ (complex (parse-hex-float "-0x1.74e7ed2b9189fp-22")
+ (parse-hex-float "0x1.3d80439e9a119p-731"))
+ (complex (parse-hex-float "-0x1.3b35ed806ae5ap-333")
+ (parse-hex-float "-0x0.05e01bcbfd9f6p-1022"))
+ 53)
+ ;; 16
+ ;; Iteration 4
+ (frob cdiv.mcgehearty-iteration.4
+ (complex (parse-hex-float "-0x1.f5c75c69829f0p-530")
+ (parse-hex-float "-0x1.e73b1fde6b909p+316"))
+ (complex (parse-hex-float "-0x1.ff96c3957742bp+1023")
+ (parse-hex-float "0x1.5bd78c9335899p+1021"))
+ (complex (parse-hex-float "-0x1.423c6ce00c73bp-710")
+ (parse-hex-float "0x1.d9edcf45bcb0ep-708"))
+ 52))
+
+(define-test complex-division.misc
+ (:tag :issue)
+ (let ((num '(1
+ 1/2
+ 1.0
+ 1d0
+ #c(1 2)
+ #c(1.0 2.0)
+ #c(1d0 2d0)
+ #c(1w0 2w0))))
+ ;; Try all combinations of divisions of different types. This is
+ ;; primarily to test that we got all the numeric contagion cases
+ ;; for division in CL:/.
+ (dolist (x num)
+ (dolist (y num)
+ (assert-true (/ x y)
+ x y)))))
+
+(define-test complex-division.single
+ (:tag :issues)
+ (let* ((x #c(1 2))
+ (y (complex (expt 2 127) (expt 2 127)))
+ (expected (coerce (/ x y)
+ '(complex single-float))))
+ ;; A naive implementation of complex division would cause an
+ ;; overflow in computing the denominator.
+ (assert-equal expected
+ (/ (coerce x '(complex single-float))
+ (coerce y '(complex single-float)))
+ x
+ y)))
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/97824b42c485a2316a2c91…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/97824b42c485a2316a2c91…
You're receiving this email because of your account on gitlab.common-lisp.net.
1
0
[Git][cmucl/cmucl][issue-459-more-accurate-dd-complex-div] Add cdiv-dd test code
by Raymond Toy (@rtoy) 08 Jan '26
by Raymond Toy (@rtoy) 08 Jan '26
08 Jan '26
Raymond Toy pushed to branch issue-459-more-accurate-dd-complex-div at cmucl / cmucl
Commits:
78e72018 by Raymond Toy at 2026-01-07T20:30:11-08:00
Add cdiv-dd test code
Remove the old test code and add new test code that fits in with the
current scheme used for cdiv-double-float.
- - - - -
1 changed file:
- tests/float.lisp
Changes:
=====================================
tests/float.lisp
=====================================
@@ -374,37 +374,32 @@
(min (rerr (realpart computed) (realpart expected))
(rerr (imagpart computed) (imagpart expected)))))
-(define-test complex-division.double-double
- (:tag :issues)
- (loop for k from 1
- for test in *test-cases*
- do
- (destructuring-bind (x y z-true expected-rel expected-rel-w)
- test
- (declare (ignore expected-rel z-true))
- (flet ((compute-true (a b)
- ;; Convert a and b to complex rationals, do the
- ;; division and convert back to get the true
- ;; expected result.
- (coerce
- (/ (complex (rational (realpart a))
- (rational (imagpart a)))
- (complex (rational (realpart b))
- (rational (imagpart b))))
- '(complex ext:double-double-float))))
- (let* ((z (/ (coerce x '(complex ext:double-double-float))
- (coerce y '(complex ext:double-double-float))))
- (z-true (compute-true x y))
- (rel (rel-err z z-true)))
- (assert-equal expected-rel-w
- rel
- k x y z z-true rel))))))
(defun do-cdiv-test (x y z-true expected-rel)
(let* ((z (/ x y))
(rel (rel-err z z-true)))
(assert-equal expected-rel
rel
x y z z-true rel)))
+
+(defun do-cdiv-dd-test (x y z-true expected-rel)
+ (flet ((compute-true (a b)
+ ;; Convert a and b to complex rationals, do the
+ ;; division and convert back to get the true
+ ;; expected result.
+ (coerce
+ (/ (complex (rational (realpart a))
+ (rational (imagpart a)))
+ (complex (rational (realpart b))
+ (rational (imagpart b))))
+ '(complex ext:double-double-float))))
+ (let* ((z (/ (coerce x '(complex ext:double-double-float))
+ (coerce y '(complex ext:double-double-float))))
+ (z-true (compute-true x y))
+ (rel (rel-err z z-true)))
+ (assert-equal expected-rel
+ rel
+ k x y z z-true rel))))
+
;; Issue #456: improve accuracy of division of complex double-floats.
;;
;; Tests for complex division. Tests 1-10 are from Baudin and Smith.
@@ -412,10 +407,11 @@
;; ansi-tests where (/ z z) didn't produce exactly 1. Tests 13-16 are
;; for examples for improvement iterations 1-4 from McGehearty.
(macrolet
- ((frob (name x y z-true rel)
+ ((frob (name x y z-true rel dd-rel)
`(define-test ,name
(:tag :issues)
- (do-cdiv-test ,x ,y ,z-true ,rel))))
+ (do-cdiv-test ,x ,y ,z-true ,rel)
+ (do-cdiv-dd-test ,x ,y ,z-true ,dd-rel))))
;; First cases are from Baudin and Smith
;; 1
(frob cdiv.baudin-case.1
@@ -423,61 +419,71 @@
(complex 1d0 (scale-float 1d0 1023))
(complex (scale-float 1d0 -1023)
(scale-float -1d0 -1023))
- 53)
+ 53
+ 106)
;; 2
(frob cdiv.baudin-case.2
(complex 1d0 1d0)
(complex (scale-float 1d0 -1023) (scale-float 1d0 -1023))
(complex (scale-float 1d0 1023) 0)
- 53)
+ 53
+ 106)
;; 3
(frob cdiv.baudin-case.3
(complex (scale-float 1d0 1023) (scale-float 1d0 -1023))
(complex (scale-float 1d0 677) (scale-float 1d0 -677))
(complex (scale-float 1d0 346) (scale-float -1d0 -1008))
- 53)
+ 53
+ 106)
;; 4
(frob cdiv.baudin-case.4.overflow
(complex (scale-float 1d0 1023) (scale-float 1d0 1023))
(complex 1d0 1d0)
(complex (scale-float 1d0 1023) 0)
- 53)
+ 53
+ 106)
;; 5
(frob cdiv.baudin-case.5.underflow-ratio
(complex (scale-float 1d0 1020) (scale-float 1d0 -844))
(complex (scale-float 1d0 656) (scale-float 1d0 -780))
(complex (scale-float 1d0 364) (scale-float -1d0 -1072))
- 53)
+ 53
+ 106)
;; 6
(frob cdiv.baudin-case.6.underflow-realpart
(complex (scale-float 1d0 -71) (scale-float 1d0 1021))
(complex (scale-float 1d0 1001) (scale-float 1d0 -323))
(complex (scale-float 1d0 -1072) (scale-float 1d0 20))
- 53)
+ 53
+ 106)
;; 7
(frob cdiv.baudin-case.7.overflow-both-parts
(complex (scale-float 1d0 -347) (scale-float 1d0 -54))
(complex (scale-float 1d0 -1037) (scale-float 1d0 -1058))
(complex 3.898125604559113300d289 8.174961907852353577d295)
- 53)
+ 53
+ 106)
;; 8
(frob cdiv.baudin-case.8
(complex (scale-float 1d0 -1074) (scale-float 1d0 -1074))
(complex (scale-float 1d0 -1073) (scale-float 1d0 -1074))
(complex 0.6d0 0.2d0)
- 53)
+ 53
+ 106)
;; 9
(frob cdiv.baudin-case.9
(complex (scale-float 1d0 1015) (scale-float 1d0 -989))
(complex (scale-float 1d0 1023) (scale-float 1d0 1023))
(complex 0.001953125d0 -0.001953125d0)
- 53)
+ 53
+ 106)
;; 10
(frob cdiv.baudin-case.10.improve-imagpart-accuracy
(complex (scale-float 1d0 -622) (scale-float 1d0 -1071))
(complex (scale-float 1d0 -343) (scale-float 1d0 -798))
(complex 1.02951151789360578d-84 6.97145987515076231d-220)
- 53)
+ 53
+ 106)
;; 11
;;
;; From Maxima. This was from a (private) email where Maxima used
@@ -487,7 +493,8 @@
#c(1.2d-311 5.7d-312)
#c(3.691993880674614517999740937026568563794896024143749539711267954d301
-1.753697093319947872394996242210428954266103103602859195409591583d301)
- 52)
+ 52
+ 107)
;; 12
;;
;; Found by ansi tests. z/z should be exactly 1.
@@ -495,7 +502,8 @@
#c(1.565640716292489d19 0.0d0)
#c(1.565640716292489d19 0.0d0)
#c(1d0 0)
- 53)
+ 53
+ 106)
;; 13
;; Iteration 1. Without this, we would instead return
;;
@@ -510,7 +518,8 @@
(parse-hex-float "0x1.98b3fbc1677bbp-697"))
(complex (parse-hex-float "0x1.BA8DF8075BCEEp+155")
(parse-hex-float "-0x1.A4AD628DA5B74p-895"))
- 53)
+ 53
+ 106)
;; 14
;; Iteration 2.
(frob cdiv.mcgehearty-iteration.2
@@ -520,7 +529,8 @@
(parse-hex-float "0x1.417bd33105808p-256"))
(complex (parse-hex-float "0x1.cde593daa4ffep-810")
(parse-hex-float "-0x1.179b9a63df6d3p-799"))
- 52)
+ 52
+ 106)
;; 15
;; Iteration 3
(frob cdiv.mcgehearty-iteration.3
@@ -530,7 +540,8 @@
(parse-hex-float "0x1.3d80439e9a119p-731"))
(complex (parse-hex-float "-0x1.3b35ed806ae5ap-333")
(parse-hex-float "-0x0.05e01bcbfd9f6p-1022"))
- 53)
+ 53
+ 106)
;; 16
;; Iteration 4
(frob cdiv.mcgehearty-iteration.4
@@ -540,7 +551,8 @@
(parse-hex-float "0x1.5bd78c9335899p+1021"))
(complex (parse-hex-float "-0x1.423c6ce00c73bp-710")
(parse-hex-float "0x1.d9edcf45bcb0ep-708"))
- 52))
+ 52
+ 106))
(define-test complex-division.misc
(:tag :issue)
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/commit/78e7201838cc9b7f0de878c…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/commit/78e7201838cc9b7f0de878c…
You're receiving this email because of your account on gitlab.common-lisp.net.
1
0
[Git][cmucl/cmucl][issue-456-more-accurate-complex-div] 3 commits: Add comments and docstrings
by Raymond Toy (@rtoy) 08 Jan '26
by Raymond Toy (@rtoy) 08 Jan '26
08 Jan '26
Raymond Toy pushed to branch issue-456-more-accurate-complex-div at cmucl / cmucl
Commits:
7b080380 by Raymond Toy at 2026-01-07T16:30:07-08:00
Add comments and docstrings
- - - - -
f52319ae by Raymond Toy at 2026-01-07T17:26:04-08:00
Try to give meaningful names to each cdiv test
For each of the tests in `*test-cases*`, break out the test data into
separate individual tests with test names that are (somewhat)
indicative of what's being tested. This is rather hard, and for some
of Baudin's tests, the article doesn't really seem to say.
- - - - -
6fc1b9c7 by Raymond Toy at 2026-01-07T17:28:47-08:00
Remove old code and cleanup comments
`*test-case*` can be removed now. Update/reorganize comments
appropriately.
- - - - -
2 changed files:
- src/code/numbers.lisp
- tests/float.lisp
Changes:
=====================================
src/code/numbers.lisp
=====================================
@@ -612,9 +612,14 @@
(defconstant +cdiv-be+ (/ 2 (* +cdiv-eps+ +cdiv-eps+)))
(defconstant +cdiv-2/eps+ (/ 2 +cdiv-eps+))
+;; Make these functions accessible. cdiv-double-float and
+;; cdiv-single-float are used by deftransforms. Of course, two-arg-/
+;; is the interface to division. cdiv-generic isn't used anywhere
+;; else.
(declaim (ext:start-block cdiv-double-float cdiv-single-float two-arg-/))
(defun cdiv-double-float (x y)
+ "Accurate division of complex double-float numbers x and y."
(declare (type (complex double-float) x y)
(optimize (speed 3) (safety 0)))
(labels
@@ -756,6 +761,7 @@
;; Smith's algorithm for complex division for (complex single-float).
;; We convert the parts to double-floats before computing the result.
(defun cdiv-single-float (x y)
+ "Accurate division of complex single-float numbers x and y."
(declare (type (complex single-float) x y))
(let ((a (float (realpart x) 1d0))
(b (float (imagpart x) 1d0))
@@ -776,6 +782,8 @@
;; Generic implementation of Smith's algorithm.
(defun cdiv-generic (x y)
+ "Complex division of generic numbers x and y. One of x or y should be
+ a complex."
(let ((a (realpart x))
(b (imagpart x))
(c (realpart y))
=====================================
tests/float.lisp
=====================================
@@ -369,122 +369,6 @@
1d0)
(- exp 52)))))
-;; Tests for complex division. Tests 1-10 are from Baudin and Smith.
-;; Test 11 is an example from Maxima. Test 12 is an example from the
-;; ansi-tests. Tests 13-16 are for examples for improvement
-;; iterations 1-4 from McGehearty.
-;;
-;; Each test is a list of values: x, y, z-true (the value of x/y), and
-;; the bits of accuracy.
-(defparameter *test-cases*
- (list
- ;; 1
- (list (complex 1d0 1d0)
- (complex 1d0 (scale-float 1d0 1023))
- (complex (scale-float 1d0 -1023)
- (scale-float -1d0 -1023))
- 53)
- ;; 2
- (list (complex 1d0 1d0)
- (complex (scale-float 1d0 -1023) (scale-float 1d0 -1023))
- (complex (scale-float 1d0 1023) 0)
- 53)
- ;; 3
- (list (complex (scale-float 1d0 1023) (scale-float 1d0 -1023))
- (complex (scale-float 1d0 677) (scale-float 1d0 -677))
- (complex (scale-float 1d0 346) (scale-float -1d0 -1008))
- 53)
- ;; 4
- (list (complex (scale-float 1d0 1023) (scale-float 1d0 1023))
- (complex 1d0 1d0)
- (complex (scale-float 1d0 1023) 0)
- 53)
- ;; 5
- (list (complex (scale-float 1d0 1020) (scale-float 1d0 -844))
- (complex (scale-float 1d0 656) (scale-float 1d0 -780))
- (complex (scale-float 1d0 364) (scale-float -1d0 -1072))
- 53)
- ;; 6
- (list (complex (scale-float 1d0 -71) (scale-float 1d0 1021))
- (complex (scale-float 1d0 1001) (scale-float 1d0 -323))
- (complex (scale-float 1d0 -1072) (scale-float 1d0 20))
- 53)
- ;; 7
- (list (complex (scale-float 1d0 -347) (scale-float 1d0 -54))
- (complex (scale-float 1d0 -1037) (scale-float 1d0 -1058))
- (complex 3.898125604559113300d289 8.174961907852353577d295)
- 53)
- ;; 8
- (list (complex (scale-float 1d0 -1074) (scale-float 1d0 -1074))
- (complex (scale-float 1d0 -1073) (scale-float 1d0 -1074))
- (complex 0.6d0 0.2d0)
- 53)
- ;; 9
- (list (complex (scale-float 1d0 1015) (scale-float 1d0 -989))
- (complex (scale-float 1d0 1023) (scale-float 1d0 1023))
- (complex 0.001953125d0 -0.001953125d0)
- 53)
- ;; 10
- (list (complex (scale-float 1d0 -622) (scale-float 1d0 -1071))
- (complex (scale-float 1d0 -343) (scale-float 1d0 -798))
- (complex 1.02951151789360578d-84 6.97145987515076231d-220)
- 53)
- ;; 11
- ;; From Maxima
- (list #c(5.43d-10 1.13d-100)
- #c(1.2d-311 5.7d-312)
- #c(3.691993880674614517999740937026568563794896024143749539711267954d301
- -1.753697093319947872394996242210428954266103103602859195409591583d301)
- 52)
- ;; 12
- ;; Found by ansi tests. z/z should be exactly 1.
- (list #c(1.565640716292489d19 0.0d0)
- #c(1.565640716292489d19 0.0d0)
- #c(1d0 0)
- 53)
- ;; 13
- ;; Iteration 1. Without this, we would instead return
- ;;
- ;; (complex (parse-hex-float "0x1.ba8df8075bceep+155")
- ;; (parse-hex-float "-0x1.a4ad6329485f0p-895"))
- ;;
- ;; whose imaginary part is quite a bit off.
- (list (complex (parse-hex-float "0x1.73a3dac1d2f1fp+509")
- (parse-hex-float "-0x1.c4dba4ba1ee79p-620"))
- (complex (parse-hex-float "0x1.adf526c249cf0p+353")
- (parse-hex-float "0x1.98b3fbc1677bbp-697"))
- (complex (parse-hex-float "0x1.BA8DF8075BCEEp+155")
- (parse-hex-float "-0x1.A4AD628DA5B74p-895"))
- 53)
- ;; 14
- ;; Iteration 2.
- (list (complex (parse-hex-float "-0x0.000000008e4f8p-1022")
- (parse-hex-float "0x0.0000060366ba7p-1022"))
- (complex (parse-hex-float "-0x1.605b467369526p-245")
- (parse-hex-float "0x1.417bd33105808p-256"))
- (complex (parse-hex-float "0x1.cde593daa4ffep-810")
- (parse-hex-float "-0x1.179b9a63df6d3p-799"))
- 52)
- ;; 15
- ;; Iteration 3
- (list (complex (parse-hex-float "0x1.cb27eece7c585p-355 ")
- (parse-hex-float "0x0.000000223b8a8p-1022"))
- (complex (parse-hex-float "-0x1.74e7ed2b9189fp-22")
- (parse-hex-float "0x1.3d80439e9a119p-731"))
- (complex (parse-hex-float "-0x1.3b35ed806ae5ap-333")
- (parse-hex-float "-0x0.05e01bcbfd9f6p-1022"))
- 53)
- ;; 16
- ;; Iteration 4
- (list (complex (parse-hex-float "-0x1.f5c75c69829f0p-530")
- (parse-hex-float "-0x1.e73b1fde6b909p+316"))
- (complex (parse-hex-float "-0x1.ff96c3957742bp+1023")
- (parse-hex-float "0x1.5bd78c9335899p+1021"))
- (complex (parse-hex-float "-0x1.423c6ce00c73bp-710")
- (parse-hex-float "0x1.d9edcf45bcb0ep-708"))
- 52)
- ))
-
;; Relative error in terms of bits of accuracy. This is the
;; definition used by Baudin and Smith. A result of 53 means the two
;; numbers have identical bits. For complex numbers, we use the min
@@ -498,19 +382,148 @@
(min (rerr (realpart computed) (realpart expected))
(rerr (imagpart computed) (imagpart expected)))))
+(defun do-cdiv-test (x y z-true expected-rel)
+ (let* ((z (/ x y))
+ (rel (rel-err z z-true)))
+ (assert-equal expected-rel
+ rel
+ x y z z-true rel)))
;; Issue #456: improve accuracy of division of complex double-floats.
-(define-test complex-division.double
- (:tag :issues)
- (loop for k from 1
- for test in *test-cases*
- do
- (destructuring-bind (x y z-true expected-rel)
- test
- (let* ((z (/ x y))
- (rel (rel-err z z-true)))
- (assert-equal expected-rel
- rel
- k x y z z-true diff rel)))))
+;;
+;; Tests for complex division. Tests 1-10 are from Baudin and Smith.
+;; Test 11 is an example from Maxima. Test 12 is an example from the
+;; ansi-tests where (/ z z) didn't produce exactly 1. Tests 13-16 are
+;; for examples for improvement iterations 1-4 from McGehearty.
+(macrolet
+ ((frob (name x y z-true rel)
+ `(define-test ,name
+ (:tag :issues)
+ (do-cdiv-test ,x ,y ,z-true ,rel))))
+ ;; First cases are from Baudin and Smith
+ ;; 1
+ (frob cdiv.baudin-case.1
+ (complex 1d0 1d0)
+ (complex 1d0 (scale-float 1d0 1023))
+ (complex (scale-float 1d0 -1023)
+ (scale-float -1d0 -1023))
+ 53)
+ ;; 2
+ (frob cdiv.baudin-case.2
+ (complex 1d0 1d0)
+ (complex (scale-float 1d0 -1023) (scale-float 1d0 -1023))
+ (complex (scale-float 1d0 1023) 0)
+ 53)
+ ;; 3
+ (frob cdiv.baudin-case.3
+ (complex (scale-float 1d0 1023) (scale-float 1d0 -1023))
+ (complex (scale-float 1d0 677) (scale-float 1d0 -677))
+ (complex (scale-float 1d0 346) (scale-float -1d0 -1008))
+ 53)
+ ;; 4
+ (frob cdiv.baudin-case.4.overflow
+ (complex (scale-float 1d0 1023) (scale-float 1d0 1023))
+ (complex 1d0 1d0)
+ (complex (scale-float 1d0 1023) 0)
+ 53)
+ ;; 5
+ (frob cdiv.baudin-case.5.underflow-ratio
+ (complex (scale-float 1d0 1020) (scale-float 1d0 -844))
+ (complex (scale-float 1d0 656) (scale-float 1d0 -780))
+ (complex (scale-float 1d0 364) (scale-float -1d0 -1072))
+ 53)
+ ;; 6
+ (frob cdiv.baudin-case.6.underflow-realpart
+ (complex (scale-float 1d0 -71) (scale-float 1d0 1021))
+ (complex (scale-float 1d0 1001) (scale-float 1d0 -323))
+ (complex (scale-float 1d0 -1072) (scale-float 1d0 20))
+ 53)
+ ;; 7
+ (frob cdiv.baudin-case.7.overflow-both-parts
+ (complex (scale-float 1d0 -347) (scale-float 1d0 -54))
+ (complex (scale-float 1d0 -1037) (scale-float 1d0 -1058))
+ (complex 3.898125604559113300d289 8.174961907852353577d295)
+ 53)
+ ;; 8
+ (frob cdiv.baudin-case.8
+ (complex (scale-float 1d0 -1074) (scale-float 1d0 -1074))
+ (complex (scale-float 1d0 -1073) (scale-float 1d0 -1074))
+ (complex 0.6d0 0.2d0)
+ 53)
+ ;; 9
+ (frob cdiv.baudin-case.9
+ (complex (scale-float 1d0 1015) (scale-float 1d0 -989))
+ (complex (scale-float 1d0 1023) (scale-float 1d0 1023))
+ (complex 0.001953125d0 -0.001953125d0)
+ 53)
+ ;; 10
+ (frob cdiv.baudin-case.10.improve-imagpart-accuracy
+ (complex (scale-float 1d0 -622) (scale-float 1d0 -1071))
+ (complex (scale-float 1d0 -343) (scale-float 1d0 -798))
+ (complex 1.02951151789360578d-84 6.97145987515076231d-220)
+ 53)
+ ;; 11
+ ;;
+ ;; From Maxima. This was from a (private) email where Maxima used
+ ;; CL:/ to compute the ratio but was not very accurate.
+ (frob cdiv.maxima-case
+ #c(5.43d-10 1.13d-100)
+ #c(1.2d-311 5.7d-312)
+ #c(3.691993880674614517999740937026568563794896024143749539711267954d301
+ -1.753697093319947872394996242210428954266103103602859195409591583d301)
+ 52)
+ ;; 12
+ ;;
+ ;; Found by ansi tests. z/z should be exactly 1.
+ (frob cdiv.ansi-test-z/z
+ #c(1.565640716292489d19 0.0d0)
+ #c(1.565640716292489d19 0.0d0)
+ #c(1d0 0)
+ 53)
+ ;; 13
+ ;; Iteration 1. Without this, we would instead return
+ ;;
+ ;; (complex (parse-hex-float "0x1.ba8df8075bceep+155")
+ ;; (parse-hex-float "-0x1.a4ad6329485f0p-895"))
+ ;;
+ ;; whose imaginary part is quite a bit off.
+ (frob cdiv.mcgehearty-iteration.1
+ (complex (parse-hex-float "0x1.73a3dac1d2f1fp+509")
+ (parse-hex-float "-0x1.c4dba4ba1ee79p-620"))
+ (complex (parse-hex-float "0x1.adf526c249cf0p+353")
+ (parse-hex-float "0x1.98b3fbc1677bbp-697"))
+ (complex (parse-hex-float "0x1.BA8DF8075BCEEp+155")
+ (parse-hex-float "-0x1.A4AD628DA5B74p-895"))
+ 53)
+ ;; 14
+ ;; Iteration 2.
+ (frob cdiv.mcgehearty-iteration.2
+ (complex (parse-hex-float "-0x0.000000008e4f8p-1022")
+ (parse-hex-float "0x0.0000060366ba7p-1022"))
+ (complex (parse-hex-float "-0x1.605b467369526p-245")
+ (parse-hex-float "0x1.417bd33105808p-256"))
+ (complex (parse-hex-float "0x1.cde593daa4ffep-810")
+ (parse-hex-float "-0x1.179b9a63df6d3p-799"))
+ 52)
+ ;; 15
+ ;; Iteration 3
+ (frob cdiv.mcgehearty-iteration.3
+ (complex (parse-hex-float "0x1.cb27eece7c585p-355 ")
+ (parse-hex-float "0x0.000000223b8a8p-1022"))
+ (complex (parse-hex-float "-0x1.74e7ed2b9189fp-22")
+ (parse-hex-float "0x1.3d80439e9a119p-731"))
+ (complex (parse-hex-float "-0x1.3b35ed806ae5ap-333")
+ (parse-hex-float "-0x0.05e01bcbfd9f6p-1022"))
+ 53)
+ ;; 16
+ ;; Iteration 4
+ (frob cdiv.mcgehearty-iteration.4
+ (complex (parse-hex-float "-0x1.f5c75c69829f0p-530")
+ (parse-hex-float "-0x1.e73b1fde6b909p+316"))
+ (complex (parse-hex-float "-0x1.ff96c3957742bp+1023")
+ (parse-hex-float "0x1.5bd78c9335899p+1021"))
+ (complex (parse-hex-float "-0x1.423c6ce00c73bp-710")
+ (parse-hex-float "0x1.d9edcf45bcb0ep-708"))
+ 52))
(define-test complex-division.misc
(:tag :issue)
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/262c2323e4d18089405d76…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/262c2323e4d18089405d76…
You're receiving this email because of your account on gitlab.common-lisp.net.
1
0
[Git][cmucl/cmucl][issue-456-more-accurate-complex-div] Factor out common code to cdiv-generic
by Raymond Toy (@rtoy) 07 Jan '26
by Raymond Toy (@rtoy) 07 Jan '26
07 Jan '26
Raymond Toy pushed to branch issue-456-more-accurate-complex-div at cmucl / cmucl
Commits:
262c2323 by Raymond Toy at 2026-01-07T11:02:30-08:00
Factor out common code to cdiv-generic
cdiv-generic implements Smith's algorithm for other cases with mixing
different types of reals and complexes together. But we leave the
case of a real divided by a complex as is, because it has a simpler
implementation.
- - - - -
1 changed file:
- src/code/numbers.lisp
Changes:
=====================================
src/code/numbers.lisp
=====================================
@@ -774,6 +774,25 @@
(f (float (/ (- b (* a r)) denom) 1f0)))
(complex e f))))))
+;; Generic implementation of Smith's algorithm.
+(defun cdiv-generic (x y)
+ (let ((a (realpart x))
+ (b (imagpart x))
+ (c (realpart y))
+ (d (imagpart y)))
+ (cond ((< (abs c) (abs d))
+ (let* ((r (/ c d))
+ (denom (+ (* c r) d))
+ (e (/ (+ (* a r) b) denom))
+ (f (/ (- (* b r) a) denom)))
+ (canonical-complex e f)))
+ (t
+ (let* ((r (/ d c))
+ (denom (+ c (* d r)))
+ (e (/ (+ a (* b r)) denom))
+ (f (/ (- b (* a r)) denom)))
+ (canonical-complex e f))))))
+
(defun two-arg-/ (x y)
(number-dispatch ((x number) (y number))
(float-contagion / x y (ratio integer))
@@ -799,45 +818,17 @@
(foreach (complex rational) (complex single-float) (complex double-float)
(complex double-double-float)))
;; We should do something better for double-double floats.
- (let ((a (realpart x))
- (b (imagpart x))
- (c (realpart y))
- (d (imagpart y)))
- (cond ((< (abs c) (abs d))
- (let* ((r (/ c d))
- (denom (+ (* c r) d))
- (e (/ (+ (* a r) b) denom))
- (f (/ (- (* b r) a) denom)))
- (canonical-complex e f)))
- (t
- (let* ((r (/ d c))
- (denom (+ c (* d r)))
- (e (/ (+ a (* b r)) denom))
- (f (/ (- b (* a r)) denom)))
- (canonical-complex e f))))))
+ (cdiv-generic x y))
(((foreach integer ratio single-float double-float double-double-float
(complex rational) (complex single-float) (complex double-float))
(complex double-double-float))
- (let ((a (realpart x))
- (b (imagpart x))
- (c (realpart y))
- (d (imagpart y)))
- (cond ((< (abs c) (abs d))
- (let* ((r (/ c d))
- (denom (+ (* c r) d))
- (e (/ (+ (* a r) b) denom))
- (f (/ (- (* b r) a) denom)))
- (canonical-complex e f)))
- (t
- (let* ((r (/ d c))
- (denom (+ c (* d r)))
- (e (/ (+ a (* b r)) denom))
- (f (/ (- b (* a r)) denom)))
- (canonical-complex e f))))))
+ (cdiv-generic x y))
(((foreach integer ratio single-float double-float double-double-float)
(complex rational))
+ ;; Smith's algorithm, but takes advantage of the fact that the
+ ;; numerator is a real number and not complex.
(let* ((ry (realpart y))
(iy (imagpart y)))
(if (> (abs ry) (abs iy))
@@ -853,22 +844,7 @@
(complex rational))
;; We probably don't need to do Smith's algorithm for rationals.
;; A naive implementation of coplex division has no issues.
- (let ((a (realpart x))
- (b (imagpart x))
- (c (realpart y))
- (d (imagpart y)))
- (cond ((< (abs c) (abs d))
- (let* ((r (/ c d))
- (denom (+ (* c r) d))
- (e (/ (+ (* a r) b) denom))
- (f (/ (- (* b r) a) denom)))
- (canonical-complex e f)))
- (t
- (let* ((r (/ d c))
- (denom (+ c (* d r)))
- (e (/ (+ a (* b r)) denom))
- (f (/ (- b (* a r)) denom)))
- (canonical-complex e f))))))
+ (cdiv-generic x y))
(((foreach (complex rational) (complex single-float) (complex double-float)
(complex double-double-float))
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/commit/262c2323e4d18089405d76f…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/commit/262c2323e4d18089405d76f…
You're receiving this email because of your account on gitlab.common-lisp.net.
1
0
[Git][cmucl/cmucl][issue-456-more-accurate-complex-div] 6 commits: Fix #460: Exit with error if main unit-tests fail
by Raymond Toy (@rtoy) 07 Jan '26
by Raymond Toy (@rtoy) 07 Jan '26
07 Jan '26
Raymond Toy pushed to branch issue-456-more-accurate-complex-div at cmucl / cmucl
Commits:
99d8540a by Raymond Toy at 2026-01-02T10:05:16-08:00
Fix #460: Exit with error if main unit-tests fail
- - - - -
28de6c68 by Raymond Toy at 2026-01-02T10:05:17-08:00
Merge branch 'issue-460-ci-fails-if-unit-tests-do' into 'master'
Fix #460: Exit with error if main unit-tests fail
Closes #460
See merge request cmucl/cmucl!340
- - - - -
58358927 by Raymond Toy at 2026-01-02T14:27:25-08:00
Fix #454 and #138: Signal errors for bad components for make-pathname
- - - - -
97824b42 by Raymond Toy at 2026-01-02T14:27:25-08:00
Merge branch 'issue-454-signal-error-for-bad-pathname-parts' into 'master'
Fix #454 and #138: Signal errors for bad components for make-pathname
Closes #454 and #138
See merge request cmucl/cmucl!333
- - - - -
1f8eb92e by Raymond Toy at 2026-01-07T08:00:45-08:00
Merge branch 'master' into issue-456-more-accurate-complex-div
- - - - -
7457eadb by Raymond Toy at 2026-01-07T10:21:05-08:00
Block compile cdiv with two-arg-/
To reduce consing, block compile cdiv-double-float and friends with
two-arg-/. This allows two-arg-/ to use the local-call convention to
jump directly to the no-arg parsing section, skipping the parsing of
the args.
- - - - -
7 changed files:
- bin/run-unit-tests.sh
- src/code/numbers.lisp
- src/code/pathname.lisp
- src/i18n/locale/cmucl.pot
- tests/float-x86.lisp
- tests/os.lisp
- tests/pathname.lisp
Changes:
=====================================
bin/run-unit-tests.sh
=====================================
@@ -9,6 +9,8 @@ usage() {
echo "run-tests.sh [-?h] [-d test-dir] [-l lisp] [tests]"
echo " -d test-dir Directory containing the unit test files"
echo " -l lisp Lisp to use for the tests; defaults to lisp"
+ echo " -p Skip package-local-nicknames test"
+ echo " -u Skip lisp-unit tests"
echo " -? This help message"
echo " -h This help message"
echo ""
@@ -25,11 +27,13 @@ usage() {
}
LISP=lisp
-while getopts "h?l:d:" arg
+while getopts "puh?l:d:" arg
do
case $arg in
l) LISP=$OPTARG ;;
d) TESTDIR=$OPTARG ;;
+ p) SKIP_PLN=yes ;;
+ u) SKIP_UNIT=yes ;;
h|\?) usage ;;
esac
done
@@ -43,12 +47,19 @@ mkdir test-tmp
ln -s /bin/ls test-tmp/ls-link
# Set the timestamps on 64-bit-timestamp-2038.txt and
-# 64-bit-timestamp-2106.txt. The time for the first file is a
-# negative value for a 32-bit time_t. The second file won't fit in a
-# 32-bit time_t value. It's ok if this doesn't work in general, as
-# long as it works on Linux for the stat test in tests/os.lisp.
-touch -d "1 April 2038" tests/resources/64-bit-timestamp-2038.txt
-touch -d "1 April 2106" tests/resources/64-bit-timestamp-2106.txt
+# 64-bit-timestamp-2106.txt, but only for OSes where we know this
+# works. (This is so we don't an annoying error message from touch
+# that doesn't accept the -d option, like MacOS 10.13.) The time for
+# the first file is a negative value for a 32-bit time_t. The second
+# file won't fit in a 32-bit time_t value. It's ok if this doesn't
+# work in general, as long as it works on Linux for the stat test in
+# tests/os.lisp.
+case `uname -s` in
+ Linux)
+ touch -d "1 April 2038" tests/resources/64-bit-timestamp-2038.txt
+ touch -d "1 April 2106" tests/resources/64-bit-timestamp-2106.txt
+ ;;
+esac
# Cleanup temp files and directories that we created during testing.
function cleanup {
@@ -69,39 +80,47 @@ fi
# gcc since clang isn't always available.
(cd "$TESTDIR" || exit 1 ; gcc -m32 -O3 -c test-return.c)
-if [ $# -eq 0 ]; then
- # Test directory arg for run-all-tests if a non-default
- # No args so run all the tests
- $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(cmucl-test-runner:run-all-tests ${TESTDIRARG})"
-else
- # Run selected files. Convert each file name to uppercase and append "-TESTS"
- result=""
- for f in "$@"
- do
- new=$(echo "$f" | tr '[:lower:]' '[:upper:]')
- result="$result "\"$new-TESTS\"
- done
- $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(progn (cmucl-test-runner:load-test-files) (cmucl-test-runner:run-test $result))"
+if [ "$SKIP_UNIT" != "yes" ]; then
+ if [ $# -eq 0 ]; then
+ # Test directory arg for run-all-tests if a non-default
+ # No args so run all the tests
+ $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(cmucl-test-runner:run-all-tests ${TESTDIRARG})" ||
+ exit 1
+ else
+ # Run selected files. Convert each file name to uppercase and append "-TESTS"
+ result=""
+ for f in "$@"
+ do
+ new=$(echo "$f" | tr '[:lower:]' '[:upper:]')
+ result="$result "\"$new-TESTS\"
+ done
+ # Run unit tests. Exits with a non-zero code if there's a failure.
+
+ $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(progn (cmucl-test-runner:load-test-files) (cmucl-test-runner:run-test $result))" ||
+ exit 1
+ fi
fi
## Now run tests for trivial-package-local-nicknames
-REPO=trivial-package-local-nicknames
-BRANCH=cmucl-updates
+if [ "$SKIP_PLN" != "yes" ]; then
+ REPO=trivial-package-local-nicknames
+ BRANCH=cmucl-updates
-set -x
-if [ -d ../$REPO ]; then
- (cd ../$REPO || exit 1; git stash; git checkout $BRANCH; git pull --rebase)
-else
- (cd ..; git clone https://gitlab.common-lisp.net/cmucl/$REPO.git)
-fi
+ set -x
+ if [ -d ../$REPO ]; then
+ (cd ../$REPO || exit 1; git stash; git checkout $BRANCH; git pull --rebase)
+ else
+ (cd ..; git clone https://gitlab.common-lisp.net/cmucl/$REPO.git)
+ fi
-LISP=$PWD/$LISP
-cd ../$REPO || exit 1
-git checkout $BRANCH
+ LISP=$PWD/$LISP
+ cd ../$REPO || exit 1
+ git checkout $BRANCH
-# Run the tests. Exits with a non-zero code if there's a failure.
-$LISP -noinit -nositeinit -batch <<'EOF'
+ # Run the tests. Exits with a non-zero code if there's a failure.
+ $LISP -noinit -nositeinit -batch <<'EOF'
(require :asdf)
(push (default-directory) asdf:*central-registry*)
(asdf:test-system :trivial-package-local-nicknames)
EOF
+fi
=====================================
src/code/numbers.lisp
=====================================
@@ -602,154 +602,156 @@
;; In particular iteration 1 and 3 are added. Iteration 2 and 4 were
;; not added. The test examples from iteration 2 and 4 didn't change
;; with or without changes added.
-(let* ((+rmin+ least-positive-normalized-double-float)
- (+rbig+ (/ most-positive-double-float 2))
- (+rmin2+ (scale-float 1d0 -53))
- (+rminscal+ (scale-float 1d0 51))
- (+rmax2+ (* +rbig+ +rmin2+))
- ;; The value of %eps in Scilab
- (+eps+ (scale-float 1d0 -52))
- (+be+ (/ 2 (* +eps+ +eps+)))
- (+2/eps+ (/ 2 +eps+)))
- (declare (double-float +rmin+ +rbig+ +rmin2+ +rminscal+ +rmax2+
- +eps+ +be+ +2/eps+))
- (defun cdiv-double-float (x y)
- (declare (type (complex double-float) x y)
- (optimize (speed 3) (safety 0)))
- (labels
- ((internal-compreal (a b c d r tt)
- (declare (double-float a b c d r tt))
- ;; Compute the real part of the complex division
- ;; (a+ib)/(c+id), assuming |d| <= |c|. r = d/c and tt = 1/(c+d*r).
- ;;
- ;; The realpart is (a*c+b*d)/(c^2+d^2).
- ;;
- ;; c^2+d^2 = c*(c+d*(d/c)) = c*(c+d*r)
- ;;
- ;; Then
- ;;
- ;; (a*c+b*d)/(c^2+d^2) = (a*c+b*d)/(c*(c+d*r))
- ;; = (a + b*d/c)/(c+d*r)
- ;; = (a + b*r)/(c + d*r).
- ;;
- ;; Thus tt = (c + d*r).
- (cond ((>= (abs r) +rmin+)
- (let ((br (* b r)))
- (if (/= br 0d0)
- (/ (+ a br) tt)
- ;; b*r underflows. Instead, compute
- ;;
- ;; (a + b*r)*tt = a*tt + b*tt*r
- ;; = a*tt + (b*tt)*r
- ;; (a + b*r)/tt = a/tt + b/tt*r
- ;; = a*tt + (b*tt)*r
- (+ (/ a tt)
- (* (/ b tt)
- r)))))
- (t
- ;; r = 0 so d is very tiny compared to c.
- ;;
- (/ (+ a (* d (/ b c)))
- tt))))
- (robust-subinternal (a b c d)
- (declare (double-float a b c d))
- (let* ((r (/ d c))
- (tt (+ c (* d r))))
- ;; e is the real part and f is the imaginary part. We
- ;; can use internal-compreal for the imaginary part by
- ;; noticing that the imaginary part of (a+i*b)/(c+i*d) is
- ;; the same as the real part of (b-i*a)/(c+i*d).
- (let ((e (internal-compreal a b c d r tt))
- (f (internal-compreal b (- a) c d r tt)))
- (values e
- f))))
+(defconstant +cdiv-rmin+ least-positive-normalized-double-float)
+(defconstant +cdiv-rbig+ (/ most-positive-double-float 2))
+(defconstant +cdiv-rmin2+ (scale-float 1d0 -53))
+(defconstant +cdiv-rminscal+ (scale-float 1d0 51))
+(defconstant +cdiv-rmax2+ (* +cdiv-rbig+ +cdiv-rmin2+))
+;; This is the value of %eps from Scilab
+(defconstant +cdiv-eps+ (scale-float 1d0 -52))
+(defconstant +cdiv-be+ (/ 2 (* +cdiv-eps+ +cdiv-eps+)))
+(defconstant +cdiv-2/eps+ (/ 2 +cdiv-eps+))
+
+(declaim (ext:start-block cdiv-double-float cdiv-single-float two-arg-/))
+
+(defun cdiv-double-float (x y)
+ (declare (type (complex double-float) x y)
+ (optimize (speed 3) (safety 0)))
+ (labels
+ ((internal-compreal (a b c d r tt)
+ (declare (double-float a b c d r tt))
+ ;; Compute the real part of the complex division
+ ;; (a+ib)/(c+id), assuming |c| <= |d|. r = d/c and tt = 1/(c+d*r).
+ ;;
+ ;; The realpart is (a*c+b*d)/(c^2+d^2).
+ ;;
+ ;; c^2+d^2 = c*(c+d*(d/c)) = c*(c+d*r)
+ ;;
+ ;; Then
+ ;;
+ ;; (a*c+b*d)/(c^2+d^2) = (a*c+b*d)/(c*(c+d*r))
+ ;; = (a + b*d/c)/(c+d*r)
+ ;; = (a + b*r)/(c + d*r).
+ ;;
+ ;; Thus tt = (c + d*r).
+ (cond ((>= (abs r) +cdiv-rmin+)
+ (let ((br (* b r)))
+ (if (/= br 0)
+ (/ (+ a br) tt)
+ ;; b*r underflows. Instead, compute
+ ;;
+ ;; (a + b*r)*tt = a*tt + b*tt*r
+ ;; = a*tt + (b*tt)*r
+ ;; (a + b*r)/tt = a/tt + b/tt*r
+ ;; = a*tt + (b*tt)*r
+ (+ (/ a tt)
+ (* (/ b tt)
+ r)))))
+ (t
+ ;; r = 0 so d is very tiny compared to c.
+ ;;
+ (/ (+ a (* d (/ b c)))
+ tt))))
+ (robust-subinternal (a b c d)
+ (declare (double-float a b c d))
+ (let* ((r (/ d c))
+ (tt (+ c (* d r))))
+ ;; e is the real part and f is the imaginary part. We
+ ;; can use internal-compreal for the imaginary part by
+ ;; noticing that the imaginary part of (a+i*b)/(c+i*d) is
+ ;; the same as the real part of (b-i*a)/(c+i*d).
+ (let ((e (internal-compreal a b c d r tt))
+ (f (internal-compreal b (- a) c d r tt)))
+ (values e
+ f))))
- (robust-internal (x y)
- (declare (type (complex double-float) x y))
- (let ((a (realpart x))
- (b (imagpart x))
- (c (realpart y))
- (d (imagpart y)))
- (declare (double-float a b c d))
- (flet ((maybe-scale (abs-tst a b c d)
- (declare (double-float a b c d))
- ;; This implements McGehearty's iteration 3 to
- ;; handle the case when some values are too big
- ;; and should be scaled down. Also if some
- ;; values are too tiny, scale them up.
- (let ((abs-a (abs a))
- (abs-b (abs b)))
- (if (or (> abs-tst +rbig+)
- (> abs-a +rbig+)
- (> abs-b +rbig+))
- (setf a (* a 0.5d0)
- b (* b 0.5d0)
- c (* c 0.5d0)
- d (* d 0.5d0))
- (if (< abs-tst +rmin2+)
- (setf a (* a +rminscal+)
- b (* b +rminscal+)
- c (* c +rminscal+)
- d (* d +rminscal+))
- (if (or (and (< abs-a +rmin+)
- (< abs-b +rmax2+)
- (< abs-tst +rmax2+))
- (and (< abs-b +rmin+)
- (< abs-a +rmax2+)
- (< abs-tst +rmax2+)))
- (setf a (* a +rminscal+)
- b (* b +rminscal+)
- c (* c +rminscal+)
- d (* d +rminscal+)))))
- (values a b c d))))
- (cond
- ((<= (abs d) (abs c))
- ;; |d| <= |c|, so we can use robust-subinternal to
- ;; perform the division.
- (multiple-value-bind (a b c d)
- (maybe-scale (abs c) a b c d)
- (multiple-value-bind (e f)
- (robust-subinternal a b c d)
- (complex e f))))
- (t
- ;; |d| > |c|. So, instead compute
- ;;
- ;; (b + i*a)/(d + i*c)
- ;; = ((b*d+a*c) + (a*d-b*c)*i)/(d^2+c^2)
- ;;
- ;; Compare this to (a+i*b)/(c+i*d) and we see that
- ;; realpart of the former is the same, but the
- ;; imagpart of the former is the negative of the
- ;; desired division.
- (multiple-value-bind (a b c d)
- (maybe-scale (abs d) a b c d)
- (multiple-value-bind (e f)
- (robust-subinternal b a d c)
- (complex e (- f))))))))))
- (let* ((max-ab (max (abs (realpart x))
- (abs (imagpart x))))
- (max-cd (max (abs (realpart y))
- (abs (imagpart y))))
- (s 1d0))
- (declare (double-float s))
- ;; If a or b is big, scale down a and b.
- (when (>= max-ab +rbig+)
- (setf x (/ x 2d0)
- s (* s 2d0)))
- ;; If c or d is big, scale down c and d.
- (when (>= max-cd +rbig+)
- (setf y (/ y 2d0)
- s (/ s 2d0)))
- ;; If a or b is tiny, scale up a and b.
- (when (<= max-ab (* +rmin+ +2/eps+))
- (setf x (* x +be+)
- s (/ s +be+)))
- ;; If c or d is tiny, scale up c and d.
- (when (<= max-cd (* +rmin+ +2/eps+))
- (setf y (* y +be+)
- s (* s +be+)))
- (* s
- (robust-internal x y))))))
+ (robust-internal (x y)
+ (declare (type (complex double-float) x y))
+ (let ((a (realpart x))
+ (b (imagpart x))
+ (c (realpart y))
+ (d (imagpart y)))
+ (declare (double-float a b c d))
+ (flet ((maybe-scale (abs-tst a b c d)
+ (declare (double-float a b c d))
+ ;; This implements McGehearty's iteration 3 to
+ ;; handle the case when some values are too big
+ ;; and should be scaled down. Also if some
+ ;; values are too tiny, scale them up.
+ (let ((abs-a (abs a))
+ (abs-b (abs b)))
+ (if (or (> abs-tst +cdiv-rbig+)
+ (> abs-a +cdiv-rbig+)
+ (> abs-b +cdiv-rbig+))
+ (setf a (* a 0.5d0)
+ b (* b 0.5d0)
+ c (* c 0.5d0)
+ d (* d 0.5d0))
+ (if (< abs-tst +cdiv-rmin2+)
+ (setf a (* a +cdiv-rminscal+)
+ b (* b +cdiv-rminscal+)
+ c (* c +cdiv-rminscal+)
+ d (* d +cdiv-rminscal+))
+ (if (or (and (< abs-a +cdiv-rmin+)
+ (< abs-b +cdiv-rmax2+)
+ (< abs-tst +cdiv-rmax2+))
+ (and (< abs-b +cdiv-rmin+)
+ (< abs-a +cdiv-rmax2+)
+ (< abs-tst +cdiv-rmax2+)))
+ (setf a (* a +cdiv-rminscal+)
+ b (* b +cdiv-rminscal+)
+ c (* c +cdiv-rminscal+)
+ d (* d +cdiv-rminscal+)))))
+ (values a b c d))))
+ (cond
+ ((<= (abs d) (abs c))
+ ;; |d| <= |c|, so we can use robust-subinternal to
+ ;; perform the division.
+ (multiple-value-bind (a b c d)
+ (maybe-scale (abs c) a b c d)
+ (multiple-value-bind (e f)
+ (robust-subinternal a b c d)
+ (complex e f))))
+ (t
+ ;; |d| > |c|. So, instead compute
+ ;;
+ ;; (b + i*a)/(d + i*c) = ((b*d+a*c) + (a*d-b*c)*i)/(d^2+c^2)
+ ;;
+ ;; Compare this to (a+i*b)/(c+i*d) and we see that
+ ;; realpart of the former is the same, but the
+ ;; imagpart of the former is the negative of the
+ ;; desired division.
+ (multiple-value-bind (a b c d)
+ (maybe-scale (abs d) a b c d)
+ (multiple-value-bind (e f)
+ (robust-subinternal b a d c)
+ (complex e (- f))))))))))
+ (let* ((a (realpart x))
+ (b (imagpart x))
+ (c (realpart y))
+ (d (imagpart y))
+ (ab (max (abs a) (abs b)))
+ (cd (max (abs c) (abs d)))
+ (s 1d0))
+ (declare (double-float s))
+ ;; If a or b is big, scale down a and b.
+ (when (>= ab +cdiv-rbig+)
+ (setf x (/ x 2)
+ s (* s 2)))
+ ;; If c or d is big, scale down c and d.
+ (when (>= cd +cdiv-rbig+)
+ (setf y (/ y 2)
+ s (/ s 2)))
+ ;; If a or b is tiny, scale up a and b.
+ (when (<= ab (* +cdiv-rmin+ +cdiv-2/eps+))
+ (setf x (* x +cdiv-be+)
+ s (/ s +cdiv-be+)))
+ ;; If c or d is tiny, scale up c and d.
+ (when (<= cd (* +cdiv-rmin+ +cdiv-2/eps+))
+ (setf y (* y +cdiv-be+)
+ s (* s +cdiv-be+)))
+ (* s
+ (robust-internal x y)))))
;; Smith's algorithm for complex division for (complex single-float).
;; We convert the parts to double-floats before computing the result.
@@ -850,7 +852,7 @@
(((complex rational)
(complex rational))
;; We probably don't need to do Smith's algorithm for rationals.
- ;; A naive implementation of complex division has no issues.
+ ;; A naive implementation of coplex division has no issues.
(let ((a (realpart x))
(b (imagpart x))
(c (realpart y))
@@ -907,6 +909,7 @@
(build-ratio (maybe-truncate nx gcd)
(* (maybe-truncate y gcd) (denominator x)))))))
+(declaim (ext:end-block))
(defun %negate (n)
(number-dispatch ((n number))
=====================================
src/code/pathname.lisp
=====================================
@@ -838,14 +838,18 @@ a host-structure or string."
(%pathname-directory defaults)
diddle-defaults)))
- ;; A bit of sanity checking on user arguments.
+ ;; A bit of sanity checking on user arguments. We don't allow a
+ ;; "/" or NUL in any string that's part of a pathname object.
(flet ((check-component-validity (name name-or-type)
(when (stringp name)
- (let ((unix-directory-separator #\/))
- (when (eq host (%pathname-host *default-pathname-defaults*))
- (when (find unix-directory-separator name)
- (warn (intl:gettext "Silly argument for a unix ~A: ~S")
- name-or-type name)))))))
+ (when (eq host (%pathname-host *default-pathname-defaults*))
+ (when (some #'(lambda (c)
+ ;; Illegal characters are a slash or NUL.
+ (case c
+ ((#\/ #\null) t)))
+ name)
+ (error _"Pathname component ~A cannot contain a slash or nul character: ~S"
+ name-or-type name))))))
(check-component-validity name :pathname-name)
(check-component-validity type :pathname-type)
(mapc #'(lambda (d)
@@ -856,8 +860,9 @@ a host-structure or string."
(not type))
(and (string= name ".")
(not type))))
- ;;
- (warn (intl:gettext "Silly argument for a unix PATHNAME-NAME: ~S") name)))
+ ;;
+ (cerror _"Continue anyway"
+ _"PATHNAME-NAME cannot be \".\" or \"..\"")))
;; More sanity checking
(when dir
=====================================
src/i18n/locale/cmucl.pot
=====================================
@@ -7717,7 +7717,7 @@ msgstr ""
msgid ", type="
msgstr ""
-#: src/code/print.lisp
+#: src/code/pathname.lisp src/code/print.lisp
msgid "Continue anyway"
msgstr ""
@@ -9785,17 +9785,17 @@ msgid "~S is not allowed as a directory component."
msgstr ""
#: src/code/pathname.lisp
-msgid ""
-"Makes a new pathname from the component arguments. Note that host is\n"
-"a host-structure or string."
+msgid "Pathname component ~A cannot contain a slash or nul character: ~S"
msgstr ""
#: src/code/pathname.lisp
-msgid "Silly argument for a unix ~A: ~S"
+msgid "PATHNAME-NAME cannot be \".\" or \"..\""
msgstr ""
#: src/code/pathname.lisp
-msgid "Silly argument for a unix PATHNAME-NAME: ~S"
+msgid ""
+"Makes a new pathname from the component arguments. Note that host is\n"
+"a host-structure or string."
msgstr ""
#: src/code/pathname.lisp
=====================================
tests/float-x86.lisp
=====================================
@@ -5,6 +5,11 @@
(in-package "FLOAT-X86-TESTS")
+;; This tests the floating-point modes for x86. This works only if we
+;; have the feature :sse2 but not :darwin since darwin has always used
+;; sse2 and not x87. But see also how FLOATING-POINT-MODES is
+;; implemented in src/code/float-trap.lisp.
+#+(and sse2 (not darwin))
(define-test set-floating-point-modes
(let ((old-x87-modes (x86::x87-floating-point-modes))
(old-sse2-modes (x86::sse2-floating-point-modes))
=====================================
tests/os.lisp
=====================================
@@ -51,6 +51,7 @@
(assert-equal 2153718000 st-atime)
(assert-equal 2153718000 st-mtime))))
+#+linux
(define-test stat.64-bit-timestamp-2106
(:tag :issues)
(let ((test-file #.(merge-pathnames "resources/64-bit-timestamp-2106.txt"
=====================================
tests/pathname.lisp
=====================================
@@ -153,4 +153,30 @@
;; Now recursively delete the directory.
(assert-true (ext:delete-directory (merge-pathnames "tmp/" path)
:recursive t))
- (assert-false (directory "tmp/")))))
+ (assert-false (directory (merge-pathnames "tmp/" path))))))
+
+(define-test issue.454.illegal-pathname-chars
+ (:tag :issues)
+ ;; A slash (Unix directory separater) is not allowed.
+ (assert-error 'simple-error
+ (make-pathname :name "a/b"))
+ (assert-error 'simple-error
+ (make-pathname :type "a/b"))
+ (assert-error 'simple-error
+ (make-pathname :directory '(:relative "a/b")))
+ ;; ASCII NUL characters are not allowed in Unix pathnames.
+ (let ((string-with-nul (concatenate 'string "a" (string #\nul) "b")))
+ (assert-error 'simple-error
+ (make-pathname :name string-with-nul))
+ (assert-error 'simple-error
+ (make-pathname :type string-with-nul))
+ (assert-error 'simple-error
+ (make-pathname :directory (list :relative string-with-nul)))))
+
+(define-test issue.454.illegal-pathname-dot
+ (:tag :issues)
+ (assert-error 'simple-error
+ (make-pathname :name "."))
+ (assert-error 'simple-error
+ (make-pathname :name "..")))
+
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/ef99f00954a34ece65ebdb…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/ef99f00954a34ece65ebdb…
You're receiving this email because of your account on gitlab.common-lisp.net.
1
0
[Git][cmucl/cmucl][issue-459-more-accurate-dd-complex-div] 9 commits: Add fixed issue #458 to release notes
by Raymond Toy (@rtoy) 03 Jan '26
by Raymond Toy (@rtoy) 03 Jan '26
03 Jan '26
Raymond Toy pushed to branch issue-459-more-accurate-dd-complex-div at cmucl / cmucl
Commits:
f146f87f by Raymond Toy at 2025-12-13T15:11:21-08:00
Add fixed issue #458 to release notes
Forgot to do that.
[skip-ci]
- - - - -
2c36bac8 by Raymond Toy at 2025-12-15T17:34:46-08:00
Use (new) Ubuntu VM to run CI instead of OpenSUSE
- - - - -
7ebc2654 by Raymond Toy at 2025-12-15T17:34:46-08:00
Merge branch 'rtoy-add-ubuntu-runner' into 'master'
Use (new) Ubuntu VM to run CI instead of OpenSUSE
See merge request cmucl/cmucl!339
- - - - -
e65f9f5c by Raymond Toy at 2025-12-17T07:04:10-08:00
Fix #457: delete-directory signals errors
- - - - -
6034f935 by Raymond Toy at 2025-12-17T07:04:10-08:00
Merge branch 'issue-457-delete-directory-signals-errors' into 'master'
Fix #457: delete-directory signals errors
Closes #457
See merge request cmucl/cmucl!336
- - - - -
10efd37c by Raymond Toy at 2025-12-17T07:12:47-08:00
Remove extra blank lines
Addresses comment from !337:
https://gitlab.common-lisp.net/cmucl/cmucl/-/merge_requests/337#note_18529
Don't need to run CI for this.
[skip-ci]
- - - - -
99d8540a by Raymond Toy at 2026-01-02T10:05:16-08:00
Fix #460: Exit with error if main unit-tests fail
- - - - -
28de6c68 by Raymond Toy at 2026-01-02T10:05:17-08:00
Merge branch 'issue-460-ci-fails-if-unit-tests-do' into 'master'
Fix #460: Exit with error if main unit-tests fail
Closes #460
See merge request cmucl/cmucl!340
- - - - -
e6f0f57e by Raymond Toy at 2026-01-03T08:07:42-08:00
Merge branch 'master' into issue-459-more-accurate-dd-complex-div
- - - - -
9 changed files:
- .gitlab-ci.yml
- bin/run-unit-tests.sh
- src/code/exports.lisp
- src/code/extensions.lisp
- src/general-info/release-22a.md
- src/i18n/locale/cmucl.pot
- tests/float-x86.lisp
- tests/os.lisp
- tests/pathname.lisp
Changes:
=====================================
.gitlab-ci.yml
=====================================
@@ -19,6 +19,8 @@ variables:
script:
- echo PATH = $PATH
- ls -F /usr/local/bin
+ # Make sure gitlab-runner is available because it's needed by the
+ # VM to upload artifacts.
- type -all gitlab-runner
# Download binaries. (Do we really need the extras tarball?)
- $CURL -o cmucl-$version-$osname.tar.$tar_ext $download_url/cmucl-$version-$osname.tar.$tar_ext
@@ -318,38 +320,38 @@ linux:static-analyzer:
- make -C build-4/lisp ANALYZER=-fanalyzer > analyzer.log 2>&1
#### OpenSUSE jobs ####
-opensuse:install:
+ubuntu:install:
<<: *install_configuration
tags:
- - opensuse
+ - ubuntu
variables:
osname: "linux"
CURL: "curl"
-opensuse:build:
+ubuntu:build:
<<: *build_configuration
tags:
- - opensuse
+ - ubuntu
needs:
- - job: opensuse:install
+ - job: ubuntu:install
artifacts: true
-opensuse:test:
+ubuntu:test:
<<: *unit_test_configuration
tags:
- - opensuse
+ - ubuntu
needs:
# Needs artifacts from build (dist/)
- - job: opensuse:build
+ - job: ubuntu:build
artifacts: true
-opensuse:ansi-test:
+ubuntu:ansi-test:
<<: *ansi_test_configuration
tags:
- - opensuse
+ - ubuntu
needs:
# Needs artifacts from build (dist/)
- - job: opensuse:build
+ - job: ubuntu:build
artifacts: true
# Optional job that runs the markdown link checker. This is optional
=====================================
bin/run-unit-tests.sh
=====================================
@@ -9,6 +9,8 @@ usage() {
echo "run-tests.sh [-?h] [-d test-dir] [-l lisp] [tests]"
echo " -d test-dir Directory containing the unit test files"
echo " -l lisp Lisp to use for the tests; defaults to lisp"
+ echo " -p Skip package-local-nicknames test"
+ echo " -u Skip lisp-unit tests"
echo " -? This help message"
echo " -h This help message"
echo ""
@@ -25,11 +27,13 @@ usage() {
}
LISP=lisp
-while getopts "h?l:d:" arg
+while getopts "puh?l:d:" arg
do
case $arg in
l) LISP=$OPTARG ;;
d) TESTDIR=$OPTARG ;;
+ p) SKIP_PLN=yes ;;
+ u) SKIP_UNIT=yes ;;
h|\?) usage ;;
esac
done
@@ -43,12 +47,19 @@ mkdir test-tmp
ln -s /bin/ls test-tmp/ls-link
# Set the timestamps on 64-bit-timestamp-2038.txt and
-# 64-bit-timestamp-2106.txt. The time for the first file is a
-# negative value for a 32-bit time_t. The second file won't fit in a
-# 32-bit time_t value. It's ok if this doesn't work in general, as
-# long as it works on Linux for the stat test in tests/os.lisp.
-touch -d "1 April 2038" tests/resources/64-bit-timestamp-2038.txt
-touch -d "1 April 2106" tests/resources/64-bit-timestamp-2106.txt
+# 64-bit-timestamp-2106.txt, but only for OSes where we know this
+# works. (This is so we don't an annoying error message from touch
+# that doesn't accept the -d option, like MacOS 10.13.) The time for
+# the first file is a negative value for a 32-bit time_t. The second
+# file won't fit in a 32-bit time_t value. It's ok if this doesn't
+# work in general, as long as it works on Linux for the stat test in
+# tests/os.lisp.
+case `uname -s` in
+ Linux)
+ touch -d "1 April 2038" tests/resources/64-bit-timestamp-2038.txt
+ touch -d "1 April 2106" tests/resources/64-bit-timestamp-2106.txt
+ ;;
+esac
# Cleanup temp files and directories that we created during testing.
function cleanup {
@@ -69,39 +80,47 @@ fi
# gcc since clang isn't always available.
(cd "$TESTDIR" || exit 1 ; gcc -m32 -O3 -c test-return.c)
-if [ $# -eq 0 ]; then
- # Test directory arg for run-all-tests if a non-default
- # No args so run all the tests
- $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(cmucl-test-runner:run-all-tests ${TESTDIRARG})"
-else
- # Run selected files. Convert each file name to uppercase and append "-TESTS"
- result=""
- for f in "$@"
- do
- new=$(echo "$f" | tr '[:lower:]' '[:upper:]')
- result="$result "\"$new-TESTS\"
- done
- $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(progn (cmucl-test-runner:load-test-files) (cmucl-test-runner:run-test $result))"
+if [ "$SKIP_UNIT" != "yes" ]; then
+ if [ $# -eq 0 ]; then
+ # Test directory arg for run-all-tests if a non-default
+ # No args so run all the tests
+ $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(cmucl-test-runner:run-all-tests ${TESTDIRARG})" ||
+ exit 1
+ else
+ # Run selected files. Convert each file name to uppercase and append "-TESTS"
+ result=""
+ for f in "$@"
+ do
+ new=$(echo "$f" | tr '[:lower:]' '[:upper:]')
+ result="$result "\"$new-TESTS\"
+ done
+ # Run unit tests. Exits with a non-zero code if there's a failure.
+
+ $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(progn (cmucl-test-runner:load-test-files) (cmucl-test-runner:run-test $result))" ||
+ exit 1
+ fi
fi
## Now run tests for trivial-package-local-nicknames
-REPO=trivial-package-local-nicknames
-BRANCH=cmucl-updates
+if [ "$SKIP_PLN" != "yes" ]; then
+ REPO=trivial-package-local-nicknames
+ BRANCH=cmucl-updates
-set -x
-if [ -d ../$REPO ]; then
- (cd ../$REPO || exit 1; git stash; git checkout $BRANCH; git pull --rebase)
-else
- (cd ..; git clone https://gitlab.common-lisp.net/cmucl/$REPO.git)
-fi
+ set -x
+ if [ -d ../$REPO ]; then
+ (cd ../$REPO || exit 1; git stash; git checkout $BRANCH; git pull --rebase)
+ else
+ (cd ..; git clone https://gitlab.common-lisp.net/cmucl/$REPO.git)
+ fi
-LISP=$PWD/$LISP
-cd ../$REPO || exit 1
-git checkout $BRANCH
+ LISP=$PWD/$LISP
+ cd ../$REPO || exit 1
+ git checkout $BRANCH
-# Run the tests. Exits with a non-zero code if there's a failure.
-$LISP -noinit -nositeinit -batch <<'EOF'
+ # Run the tests. Exits with a non-zero code if there's a failure.
+ $LISP -noinit -nositeinit -batch <<'EOF'
(require :asdf)
(push (default-directory) asdf:*central-registry*)
(asdf:test-system :trivial-package-local-nicknames)
EOF
+fi
=====================================
src/code/exports.lisp
=====================================
@@ -1213,7 +1213,8 @@
"INVALID-FASL"
"WITH-TEMPORARY-DIRECTORY"
- "WITH-TEMPORARY-FILE")
+ "WITH-TEMPORARY-FILE"
+ "DELETE-DIRECTORY")
;; gencgc features
#+gencgc
(:export "GET-GC-ASSERTIONS"
=====================================
src/code/extensions.lisp
=====================================
@@ -673,9 +673,9 @@
(defun delete-directory (dirname &key recursive)
_N"Delete the directory Dirname. If the Recursive is non-NIL,
recursively delete the directory Dirname including all files and
- subdirectories. Dirname must be a pathname to a directory. Any NAME
- or TYPE components in Dirname are ignored."
- (declare (type pathname dirname))
+ subdirectories. Dirname must name a directory. Any NAME or TYPE
+ components in Dirname are ignored. A FILE-ERROR is signaled if any
+ directory cannot be deleted."
(when recursive
;; Find all the files or directories in DIRNAME.
(dolist (path (directory (merge-pathnames "*.*" dirname)))
@@ -685,8 +685,15 @@
(delete-directory path :recursive t)
(delete-file path))))
;; Finally delete the directory.
- (unix:unix-rmdir (namestring dirname))
- (values))
+ (multiple-value-bind (ok errno)
+ (unix:unix-rmdir (namestring dirname))
+ (unless ok
+ (error 'kernel:simple-file-error
+ :pathname dirname
+ :format-control (intl:gettext "Could not remove directory \"~A\": ~A.")
+ :format-arguments (list dirname
+ (unix:get-unix-error-msg errno))))
+ ok))
;;; WITH-TEMPORARY-DIRECTORY -- Public
=====================================
src/general-info/release-22a.md
=====================================
@@ -34,6 +34,7 @@ public domain.
* #446: Use C compiler to get errno values to update UNIX
defpackage with errno symbols
* #453: Use correct flags for analyzer and always save logs.
+ * #458: Spurious overflow in double-double-float multiply
* Other changes:
* Improvements to the PCL implementation of CLOS:
* Changes to building procedure:
=====================================
src/i18n/locale/cmucl.pot
=====================================
@@ -6011,8 +6011,13 @@ msgstr ""
msgid ""
"Delete the directory Dirname. If the Recursive is non-NIL,\n"
" recursively delete the directory Dirname including all files and\n"
-" subdirectories. Dirname must be a pathname to a directory. Any NAME\n"
-" or TYPE components in Dirname are ignored."
+" subdirectories. Dirname must name a directory. Any NAME or TYPE\n"
+" components in Dirname are ignored. A FILE-ERROR is signaled if any\n"
+" directory cannot be deleted."
+msgstr ""
+
+#: src/code/extensions.lisp
+msgid "Could not remove directory \"~A\": ~A."
msgstr ""
#: src/code/extensions.lisp
=====================================
tests/float-x86.lisp
=====================================
@@ -5,6 +5,11 @@
(in-package "FLOAT-X86-TESTS")
+;; This tests the floating-point modes for x86. This works only if we
+;; have the feature :sse2 but not :darwin since darwin has always used
+;; sse2 and not x87. But see also how FLOATING-POINT-MODES is
+;; implemented in src/code/float-trap.lisp.
+#+(and sse2 (not darwin))
(define-test set-floating-point-modes
(let ((old-x87-modes (x86::x87-floating-point-modes))
(old-sse2-modes (x86::sse2-floating-point-modes))
=====================================
tests/os.lisp
=====================================
@@ -51,6 +51,7 @@
(assert-equal 2153718000 st-atime)
(assert-equal 2153718000 st-mtime))))
+#+linux
(define-test stat.64-bit-timestamp-2106
(:tag :issues)
(let ((test-file #.(merge-pathnames "resources/64-bit-timestamp-2106.txt"
=====================================
tests/pathname.lisp
=====================================
@@ -144,14 +144,13 @@
(assert-equal dir-tilde dir-home))))
(define-test delete-directory
- (let ((dir (ensure-directories-exist "tmp/a/b/c/")))
- ;; Verify that the directories were created.
- (assert-equal "tmp/a/b/c/"
- dir)
- ;; Try to delete the directory. It should fail, which we verify
- ;; by noting the directory listing is not empty.
- (ext::delete-directory (pathname "tmp/"))
- (assert-true (directory "tmp/"))
- ;; Now recursively delete the directory.
- (ext::delete-directory (pathname "tmp/") :recursive t)
- (assert-false (directory "tmp/"))))
+ (:tag :issues)
+ (ext:with-temporary-directory (path)
+ (let ((dir (ensure-directories-exist (merge-pathnames "tmp/a/b/c/" path))))
+ ;; Try to delete the directory. It should fail..
+ (assert-error 'kernel:simple-file-error
+ (ext:delete-directory (merge-pathnames "tmp/" path)))
+ ;; Now recursively delete the directory.
+ (assert-true (ext:delete-directory (merge-pathnames "tmp/" path)
+ :recursive t))
+ (assert-false (directory "tmp/")))))
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/9f881b527331169feb61a5…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/9f881b527331169feb61a5…
You're receiving this email because of your account on gitlab.common-lisp.net.
1
0
[Git][cmucl/cmucl][master] 2 commits: Fix #454 and #138: Signal errors for bad components for make-pathname
by Raymond Toy (@rtoy) 02 Jan '26
by Raymond Toy (@rtoy) 02 Jan '26
02 Jan '26
Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
58358927 by Raymond Toy at 2026-01-02T14:27:25-08:00
Fix #454 and #138: Signal errors for bad components for make-pathname
- - - - -
97824b42 by Raymond Toy at 2026-01-02T14:27:25-08:00
Merge branch 'issue-454-signal-error-for-bad-pathname-parts' into 'master'
Fix #454 and #138: Signal errors for bad components for make-pathname
Closes #454 and #138
See merge request cmucl/cmucl!333
- - - - -
3 changed files:
- src/code/pathname.lisp
- src/i18n/locale/cmucl.pot
- tests/pathname.lisp
Changes:
=====================================
src/code/pathname.lisp
=====================================
@@ -838,14 +838,18 @@ a host-structure or string."
(%pathname-directory defaults)
diddle-defaults)))
- ;; A bit of sanity checking on user arguments.
+ ;; A bit of sanity checking on user arguments. We don't allow a
+ ;; "/" or NUL in any string that's part of a pathname object.
(flet ((check-component-validity (name name-or-type)
(when (stringp name)
- (let ((unix-directory-separator #\/))
- (when (eq host (%pathname-host *default-pathname-defaults*))
- (when (find unix-directory-separator name)
- (warn (intl:gettext "Silly argument for a unix ~A: ~S")
- name-or-type name)))))))
+ (when (eq host (%pathname-host *default-pathname-defaults*))
+ (when (some #'(lambda (c)
+ ;; Illegal characters are a slash or NUL.
+ (case c
+ ((#\/ #\null) t)))
+ name)
+ (error _"Pathname component ~A cannot contain a slash or nul character: ~S"
+ name-or-type name))))))
(check-component-validity name :pathname-name)
(check-component-validity type :pathname-type)
(mapc #'(lambda (d)
@@ -856,8 +860,9 @@ a host-structure or string."
(not type))
(and (string= name ".")
(not type))))
- ;;
- (warn (intl:gettext "Silly argument for a unix PATHNAME-NAME: ~S") name)))
+ ;;
+ (cerror _"Continue anyway"
+ _"PATHNAME-NAME cannot be \".\" or \"..\"")))
;; More sanity checking
(when dir
=====================================
src/i18n/locale/cmucl.pot
=====================================
@@ -7717,7 +7717,7 @@ msgstr ""
msgid ", type="
msgstr ""
-#: src/code/print.lisp
+#: src/code/pathname.lisp src/code/print.lisp
msgid "Continue anyway"
msgstr ""
@@ -9785,17 +9785,17 @@ msgid "~S is not allowed as a directory component."
msgstr ""
#: src/code/pathname.lisp
-msgid ""
-"Makes a new pathname from the component arguments. Note that host is\n"
-"a host-structure or string."
+msgid "Pathname component ~A cannot contain a slash or nul character: ~S"
msgstr ""
#: src/code/pathname.lisp
-msgid "Silly argument for a unix ~A: ~S"
+msgid "PATHNAME-NAME cannot be \".\" or \"..\""
msgstr ""
#: src/code/pathname.lisp
-msgid "Silly argument for a unix PATHNAME-NAME: ~S"
+msgid ""
+"Makes a new pathname from the component arguments. Note that host is\n"
+"a host-structure or string."
msgstr ""
#: src/code/pathname.lisp
=====================================
tests/pathname.lisp
=====================================
@@ -153,4 +153,30 @@
;; Now recursively delete the directory.
(assert-true (ext:delete-directory (merge-pathnames "tmp/" path)
:recursive t))
- (assert-false (directory "tmp/")))))
+ (assert-false (directory (merge-pathnames "tmp/" path))))))
+
+(define-test issue.454.illegal-pathname-chars
+ (:tag :issues)
+ ;; A slash (Unix directory separater) is not allowed.
+ (assert-error 'simple-error
+ (make-pathname :name "a/b"))
+ (assert-error 'simple-error
+ (make-pathname :type "a/b"))
+ (assert-error 'simple-error
+ (make-pathname :directory '(:relative "a/b")))
+ ;; ASCII NUL characters are not allowed in Unix pathnames.
+ (let ((string-with-nul (concatenate 'string "a" (string #\nul) "b")))
+ (assert-error 'simple-error
+ (make-pathname :name string-with-nul))
+ (assert-error 'simple-error
+ (make-pathname :type string-with-nul))
+ (assert-error 'simple-error
+ (make-pathname :directory (list :relative string-with-nul)))))
+
+(define-test issue.454.illegal-pathname-dot
+ (:tag :issues)
+ (assert-error 'simple-error
+ (make-pathname :name "."))
+ (assert-error 'simple-error
+ (make-pathname :name "..")))
+
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/28de6c68defdaec3afac11…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/28de6c68defdaec3afac11…
You're receiving this email because of your account on gitlab.common-lisp.net.
1
0
[Git][cmucl/cmucl][issue-425-correctly-rounded-math-functions] 7 commits: Fix #457: delete-directory signals errors
by Raymond Toy (@rtoy) 02 Jan '26
by Raymond Toy (@rtoy) 02 Jan '26
02 Jan '26
Raymond Toy pushed to branch issue-425-correctly-rounded-math-functions at cmucl / cmucl
Commits:
e65f9f5c by Raymond Toy at 2025-12-17T07:04:10-08:00
Fix #457: delete-directory signals errors
- - - - -
6034f935 by Raymond Toy at 2025-12-17T07:04:10-08:00
Merge branch 'issue-457-delete-directory-signals-errors' into 'master'
Fix #457: delete-directory signals errors
Closes #457
See merge request cmucl/cmucl!336
- - - - -
10efd37c by Raymond Toy at 2025-12-17T07:12:47-08:00
Remove extra blank lines
Addresses comment from !337:
https://gitlab.common-lisp.net/cmucl/cmucl/-/merge_requests/337#note_18529
Don't need to run CI for this.
[skip-ci]
- - - - -
885baf0f by Raymond Toy at 2025-12-24T12:43:58-08:00
Merge branch 'master' into issue-425-correctly-rounded-math-functions
- - - - -
99d8540a by Raymond Toy at 2026-01-02T10:05:16-08:00
Fix #460: Exit with error if main unit-tests fail
- - - - -
28de6c68 by Raymond Toy at 2026-01-02T10:05:17-08:00
Merge branch 'issue-460-ci-fails-if-unit-tests-do' into 'master'
Fix #460: Exit with error if main unit-tests fail
Closes #460
See merge request cmucl/cmucl!340
- - - - -
c97ae992 by Raymond Toy at 2026-01-02T14:03:03-08:00
Merge branch 'master' into issue-425-correctly-rounded-math-functions
- - - - -
8 changed files:
- bin/run-unit-tests.sh
- src/code/exports.lisp
- src/code/extensions.lisp
- src/i18n/locale/cmucl.pot
- tests/float-x86.lisp
- tests/float.lisp
- tests/os.lisp
- tests/pathname.lisp
Changes:
=====================================
bin/run-unit-tests.sh
=====================================
@@ -9,8 +9,8 @@ usage() {
echo "run-tests.sh [-?h] [-d test-dir] [-l lisp] [tests]"
echo " -d test-dir Directory containing the unit test files"
echo " -l lisp Lisp to use for the tests; defaults to lisp"
- echo " -u Skip lisp-unit tests"
echo " -p Skip package-local-nicknames test"
+ echo " -u Skip lisp-unit tests"
echo " -? This help message"
echo " -h This help message"
echo ""
@@ -27,13 +27,13 @@ usage() {
}
LISP=lisp
-while getopts "uph?l:d:" arg
+while getopts "puh?l:d:" arg
do
case $arg in
l) LISP=$OPTARG ;;
d) TESTDIR=$OPTARG ;;
- u) SKIP_UNIT=yes ;;
p) SKIP_PLN=yes ;;
+ u) SKIP_UNIT=yes ;;
h|\?) usage ;;
esac
done
@@ -47,12 +47,19 @@ mkdir test-tmp
ln -s /bin/ls test-tmp/ls-link
# Set the timestamps on 64-bit-timestamp-2038.txt and
-# 64-bit-timestamp-2106.txt. The time for the first file is a
-# negative value for a 32-bit time_t. The second file won't fit in a
-# 32-bit time_t value. It's ok if this doesn't work in general, as
-# long as it works on Linux for the stat test in tests/os.lisp.
-touch -d "1 April 2038" tests/resources/64-bit-timestamp-2038.txt
-touch -d "1 April 2106" tests/resources/64-bit-timestamp-2106.txt
+# 64-bit-timestamp-2106.txt, but only for OSes where we know this
+# works. (This is so we don't an annoying error message from touch
+# that doesn't accept the -d option, like MacOS 10.13.) The time for
+# the first file is a negative value for a 32-bit time_t. The second
+# file won't fit in a 32-bit time_t value. It's ok if this doesn't
+# work in general, as long as it works on Linux for the stat test in
+# tests/os.lisp.
+case `uname -s` in
+ Linux)
+ touch -d "1 April 2038" tests/resources/64-bit-timestamp-2038.txt
+ touch -d "1 April 2106" tests/resources/64-bit-timestamp-2106.txt
+ ;;
+esac
# Cleanup temp files and directories that we created during testing.
function cleanup {
@@ -95,8 +102,6 @@ if [ "$SKIP_UNIT" != "yes" ]; then
fi
## Now run tests for trivial-package-local-nicknames
-echo SKIP_PLN = $SKIP_PLN
-
if [ "$SKIP_PLN" != "yes" ]; then
REPO=trivial-package-local-nicknames
BRANCH=cmucl-updates
=====================================
src/code/exports.lisp
=====================================
@@ -1213,7 +1213,8 @@
"INVALID-FASL"
"WITH-TEMPORARY-DIRECTORY"
- "WITH-TEMPORARY-FILE")
+ "WITH-TEMPORARY-FILE"
+ "DELETE-DIRECTORY")
;; gencgc features
#+gencgc
(:export "GET-GC-ASSERTIONS"
=====================================
src/code/extensions.lisp
=====================================
@@ -673,9 +673,9 @@
(defun delete-directory (dirname &key recursive)
_N"Delete the directory Dirname. If the Recursive is non-NIL,
recursively delete the directory Dirname including all files and
- subdirectories. Dirname must be a pathname to a directory. Any NAME
- or TYPE components in Dirname are ignored."
- (declare (type pathname dirname))
+ subdirectories. Dirname must name a directory. Any NAME or TYPE
+ components in Dirname are ignored. A FILE-ERROR is signaled if any
+ directory cannot be deleted."
(when recursive
;; Find all the files or directories in DIRNAME.
(dolist (path (directory (merge-pathnames "*.*" dirname)))
@@ -685,8 +685,15 @@
(delete-directory path :recursive t)
(delete-file path))))
;; Finally delete the directory.
- (unix:unix-rmdir (namestring dirname))
- (values))
+ (multiple-value-bind (ok errno)
+ (unix:unix-rmdir (namestring dirname))
+ (unless ok
+ (error 'kernel:simple-file-error
+ :pathname dirname
+ :format-control (intl:gettext "Could not remove directory \"~A\": ~A.")
+ :format-arguments (list dirname
+ (unix:get-unix-error-msg errno))))
+ ok))
;;; WITH-TEMPORARY-DIRECTORY -- Public
=====================================
src/i18n/locale/cmucl.pot
=====================================
@@ -6011,8 +6011,13 @@ msgstr ""
msgid ""
"Delete the directory Dirname. If the Recursive is non-NIL,\n"
" recursively delete the directory Dirname including all files and\n"
-" subdirectories. Dirname must be a pathname to a directory. Any NAME\n"
-" or TYPE components in Dirname are ignored."
+" subdirectories. Dirname must name a directory. Any NAME or TYPE\n"
+" components in Dirname are ignored. A FILE-ERROR is signaled if any\n"
+" directory cannot be deleted."
+msgstr ""
+
+#: src/code/extensions.lisp
+msgid "Could not remove directory \"~A\": ~A."
msgstr ""
#: src/code/extensions.lisp
=====================================
tests/float-x86.lisp
=====================================
@@ -5,6 +5,11 @@
(in-package "FLOAT-X86-TESTS")
+;; This tests the floating-point modes for x86. This works only if we
+;; have the feature :sse2 but not :darwin since darwin has always used
+;; sse2 and not x87. But see also how FLOATING-POINT-MODES is
+;; implemented in src/code/float-trap.lisp.
+#+(and sse2 (not darwin))
(define-test set-floating-point-modes
(let ((old-x87-modes (x86::x87-floating-point-modes))
(old-sse2-modes (x86::sse2-floating-point-modes))
=====================================
tests/float.lisp
=====================================
@@ -343,8 +343,6 @@
(assert-true (typep new-mode 'x86::float-modes))
(assert-equal new-mode (setf (x86::x87-floating-point-modes) new-mode))))
-
-
;; Issue #458
(define-test dd-mult-overflow
(:tag :issues)
=====================================
tests/os.lisp
=====================================
@@ -51,6 +51,7 @@
(assert-equal 2153718000 st-atime)
(assert-equal 2153718000 st-mtime))))
+#+linux
(define-test stat.64-bit-timestamp-2106
(:tag :issues)
(let ((test-file #.(merge-pathnames "resources/64-bit-timestamp-2106.txt"
=====================================
tests/pathname.lisp
=====================================
@@ -144,14 +144,13 @@
(assert-equal dir-tilde dir-home))))
(define-test delete-directory
- (let ((dir (ensure-directories-exist "tmp/a/b/c/")))
- ;; Verify that the directories were created.
- (assert-equal "tmp/a/b/c/"
- dir)
- ;; Try to delete the directory. It should fail, which we verify
- ;; by noting the directory listing is not empty.
- (ext::delete-directory (pathname "tmp/"))
- (assert-true (directory "tmp/"))
- ;; Now recursively delete the directory.
- (ext::delete-directory (pathname "tmp/") :recursive t)
- (assert-false (directory "tmp/"))))
+ (:tag :issues)
+ (ext:with-temporary-directory (path)
+ (let ((dir (ensure-directories-exist (merge-pathnames "tmp/a/b/c/" path))))
+ ;; Try to delete the directory. It should fail..
+ (assert-error 'kernel:simple-file-error
+ (ext:delete-directory (merge-pathnames "tmp/" path)))
+ ;; Now recursively delete the directory.
+ (assert-true (ext:delete-directory (merge-pathnames "tmp/" path)
+ :recursive t))
+ (assert-false (directory "tmp/")))))
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/9c16379a168976b4d96b24…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/9c16379a168976b4d96b24…
You're receiving this email because of your account on gitlab.common-lisp.net.
1
0
[Git][cmucl/cmucl][issue-454-signal-error-for-bad-pathname-parts] 18 commits: Dummy commit to run pipeline
by Raymond Toy (@rtoy) 02 Jan '26
by Raymond Toy (@rtoy) 02 Jan '26
02 Jan '26
Raymond Toy pushed to branch issue-454-signal-error-for-bad-pathname-parts at cmucl / cmucl
Commits:
b04aaec6 by Raymond Toy at 2025-12-03T18:30:46-08:00
Dummy commit to run pipeline
Just removed an extra line at the end of the file.
- - - - -
16c334b0 by Raymond Toy at 2025-12-04T15:46:41-08:00
Undo previous change that deleted a blank line
Dummy commit to get CI to run to test out concurrent
builds. (concurrent=2).
- - - - -
25cd44a2 by Raymond Toy at 2025-12-04T17:57:49-08:00
Fix #455: Allow manual pipeline runs
- - - - -
ae1bdde7 by Raymond Toy at 2025-12-04T17:57:50-08:00
Merge branch 'issue-455-manual-pipeline-run' into 'master'
Fix #455: Allow manual pipeline runs
Closes #455
See merge request cmucl/cmucl!334
- - - - -
fd2e83a1 by Raymond Toy at 2025-12-12T15:38:35-08:00
Fix two important typos in unix-get-username and delete-directory
In `unix-get-username`, we were freeing `name` instead of `result`.
In `delete-directory`, we misspelled `recursive` as `recusive`.
Fix these typos.
- - - - -
b9494595 by Raymond Toy at 2025-12-12T15:54:05-08:00
Add some tests for unix-get-username and delete-directory
Add a simple test that unix-get-username returns something useful.
Add a test for delete-directory that tests it deletes directories as
specified, include recursively.
- - - - -
e2b4641a by Raymond Toy at 2025-12-13T14:24:22-08:00
Fix #458: Fix spurious overflow in double-double multiply
- - - - -
16097f45 by Raymond Toy at 2025-12-13T14:24:22-08:00
Merge branch 'issue-458-double-double-mult-overflow' into 'master'
Fix #458: Fix spurious overflow in double-double multiply
Closes #458
See merge request cmucl/cmucl!337
- - - - -
f146f87f by Raymond Toy at 2025-12-13T15:11:21-08:00
Add fixed issue #458 to release notes
Forgot to do that.
[skip-ci]
- - - - -
2c36bac8 by Raymond Toy at 2025-12-15T17:34:46-08:00
Use (new) Ubuntu VM to run CI instead of OpenSUSE
- - - - -
7ebc2654 by Raymond Toy at 2025-12-15T17:34:46-08:00
Merge branch 'rtoy-add-ubuntu-runner' into 'master'
Use (new) Ubuntu VM to run CI instead of OpenSUSE
See merge request cmucl/cmucl!339
- - - - -
e65f9f5c by Raymond Toy at 2025-12-17T07:04:10-08:00
Fix #457: delete-directory signals errors
- - - - -
6034f935 by Raymond Toy at 2025-12-17T07:04:10-08:00
Merge branch 'issue-457-delete-directory-signals-errors' into 'master'
Fix #457: delete-directory signals errors
Closes #457
See merge request cmucl/cmucl!336
- - - - -
10efd37c by Raymond Toy at 2025-12-17T07:12:47-08:00
Remove extra blank lines
Addresses comment from !337:
https://gitlab.common-lisp.net/cmucl/cmucl/-/merge_requests/337#note_18529
Don't need to run CI for this.
[skip-ci]
- - - - -
99d8540a by Raymond Toy at 2026-01-02T10:05:16-08:00
Fix #460: Exit with error if main unit-tests fail
- - - - -
28de6c68 by Raymond Toy at 2026-01-02T10:05:17-08:00
Merge branch 'issue-460-ci-fails-if-unit-tests-do' into 'master'
Fix #460: Exit with error if main unit-tests fail
Closes #460
See merge request cmucl/cmucl!340
- - - - -
66899cbc by Raymond Toy at 2026-01-02T12:59:03-08:00
Apply suggested change to use SOME and signal error not cerror
- - - - -
266283df by Raymond Toy at 2026-01-02T13:15:03-08:00
Merge branch 'master' into issue-454-signal-error-for-bad-pathname-parts
Fix merge conflict in tests/pathname.lisp
- - - - -
14 changed files:
- .gitlab-ci.yml
- bin/run-unit-tests.sh
- src/code/exports.lisp
- src/code/extensions.lisp
- src/code/pathname.lisp
- src/code/unix.lisp
- src/compiler/float-tran-dd.lisp
- src/general-info/release-22a.md
- src/i18n/locale/cmucl.pot
- tests/float-x86.lisp
- tests/float.lisp
- tests/os.lisp
- tests/pathname.lisp
- tests/unix.lisp
Changes:
=====================================
.gitlab-ci.yml
=====================================
@@ -19,6 +19,8 @@ variables:
script:
- echo PATH = $PATH
- ls -F /usr/local/bin
+ # Make sure gitlab-runner is available because it's needed by the
+ # VM to upload artifacts.
- type -all gitlab-runner
# Download binaries. (Do we really need the extras tarball?)
- $CURL -o cmucl-$version-$osname.tar.$tar_ext $download_url/cmucl-$version-$osname.tar.$tar_ext
@@ -133,6 +135,7 @@ linux:install:
- if: $CI_PIPELINE_SOURCE == "push"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "branch"
+ - if: $CI_PIPELINE_SOURCE == "web"
linux:build:
<<: *build_configuration
@@ -148,6 +151,7 @@ linux:build:
- if: $CI_PIPELINE_SOURCE == "schedule"
- if: $CI_PIPELINE_SOURCE == "push"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
+ - if: $CI_PIPELINE_SOURCE == "web"
linux:cross-build:
stage: build
@@ -316,38 +320,38 @@ linux:static-analyzer:
- make -C build-4/lisp ANALYZER=-fanalyzer > analyzer.log 2>&1
#### OpenSUSE jobs ####
-opensuse:install:
+ubuntu:install:
<<: *install_configuration
tags:
- - opensuse
+ - ubuntu
variables:
osname: "linux"
CURL: "curl"
-opensuse:build:
+ubuntu:build:
<<: *build_configuration
tags:
- - opensuse
+ - ubuntu
needs:
- - job: opensuse:install
+ - job: ubuntu:install
artifacts: true
-opensuse:test:
+ubuntu:test:
<<: *unit_test_configuration
tags:
- - opensuse
+ - ubuntu
needs:
# Needs artifacts from build (dist/)
- - job: opensuse:build
+ - job: ubuntu:build
artifacts: true
-opensuse:ansi-test:
+ubuntu:ansi-test:
<<: *ansi_test_configuration
tags:
- - opensuse
+ - ubuntu
needs:
# Needs artifacts from build (dist/)
- - job: opensuse:build
+ - job: ubuntu:build
artifacts: true
# Optional job that runs the markdown link checker. This is optional
=====================================
bin/run-unit-tests.sh
=====================================
@@ -9,6 +9,8 @@ usage() {
echo "run-tests.sh [-?h] [-d test-dir] [-l lisp] [tests]"
echo " -d test-dir Directory containing the unit test files"
echo " -l lisp Lisp to use for the tests; defaults to lisp"
+ echo " -p Skip package-local-nicknames test"
+ echo " -u Skip lisp-unit tests"
echo " -? This help message"
echo " -h This help message"
echo ""
@@ -25,11 +27,13 @@ usage() {
}
LISP=lisp
-while getopts "h?l:d:" arg
+while getopts "puh?l:d:" arg
do
case $arg in
l) LISP=$OPTARG ;;
d) TESTDIR=$OPTARG ;;
+ p) SKIP_PLN=yes ;;
+ u) SKIP_UNIT=yes ;;
h|\?) usage ;;
esac
done
@@ -43,12 +47,19 @@ mkdir test-tmp
ln -s /bin/ls test-tmp/ls-link
# Set the timestamps on 64-bit-timestamp-2038.txt and
-# 64-bit-timestamp-2106.txt. The time for the first file is a
-# negative value for a 32-bit time_t. The second file won't fit in a
-# 32-bit time_t value. It's ok if this doesn't work in general, as
-# long as it works on Linux for the stat test in tests/os.lisp.
-touch -d "1 April 2038" tests/resources/64-bit-timestamp-2038.txt
-touch -d "1 April 2106" tests/resources/64-bit-timestamp-2106.txt
+# 64-bit-timestamp-2106.txt, but only for OSes where we know this
+# works. (This is so we don't an annoying error message from touch
+# that doesn't accept the -d option, like MacOS 10.13.) The time for
+# the first file is a negative value for a 32-bit time_t. The second
+# file won't fit in a 32-bit time_t value. It's ok if this doesn't
+# work in general, as long as it works on Linux for the stat test in
+# tests/os.lisp.
+case `uname -s` in
+ Linux)
+ touch -d "1 April 2038" tests/resources/64-bit-timestamp-2038.txt
+ touch -d "1 April 2106" tests/resources/64-bit-timestamp-2106.txt
+ ;;
+esac
# Cleanup temp files and directories that we created during testing.
function cleanup {
@@ -69,39 +80,47 @@ fi
# gcc since clang isn't always available.
(cd "$TESTDIR" || exit 1 ; gcc -m32 -O3 -c test-return.c)
-if [ $# -eq 0 ]; then
- # Test directory arg for run-all-tests if a non-default
- # No args so run all the tests
- $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(cmucl-test-runner:run-all-tests ${TESTDIRARG})"
-else
- # Run selected files. Convert each file name to uppercase and append "-TESTS"
- result=""
- for f in "$@"
- do
- new=$(echo "$f" | tr '[:lower:]' '[:upper:]')
- result="$result "\"$new-TESTS\"
- done
- $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(progn (cmucl-test-runner:load-test-files) (cmucl-test-runner:run-test $result))"
+if [ "$SKIP_UNIT" != "yes" ]; then
+ if [ $# -eq 0 ]; then
+ # Test directory arg for run-all-tests if a non-default
+ # No args so run all the tests
+ $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(cmucl-test-runner:run-all-tests ${TESTDIRARG})" ||
+ exit 1
+ else
+ # Run selected files. Convert each file name to uppercase and append "-TESTS"
+ result=""
+ for f in "$@"
+ do
+ new=$(echo "$f" | tr '[:lower:]' '[:upper:]')
+ result="$result "\"$new-TESTS\"
+ done
+ # Run unit tests. Exits with a non-zero code if there's a failure.
+
+ $LISP -nositeinit -noinit -load "$TESTDIR"/run-tests.lisp -eval "(progn (cmucl-test-runner:load-test-files) (cmucl-test-runner:run-test $result))" ||
+ exit 1
+ fi
fi
## Now run tests for trivial-package-local-nicknames
-REPO=trivial-package-local-nicknames
-BRANCH=cmucl-updates
+if [ "$SKIP_PLN" != "yes" ]; then
+ REPO=trivial-package-local-nicknames
+ BRANCH=cmucl-updates
-set -x
-if [ -d ../$REPO ]; then
- (cd ../$REPO || exit 1; git stash; git checkout $BRANCH; git pull --rebase)
-else
- (cd ..; git clone https://gitlab.common-lisp.net/cmucl/$REPO.git)
-fi
+ set -x
+ if [ -d ../$REPO ]; then
+ (cd ../$REPO || exit 1; git stash; git checkout $BRANCH; git pull --rebase)
+ else
+ (cd ..; git clone https://gitlab.common-lisp.net/cmucl/$REPO.git)
+ fi
-LISP=$PWD/$LISP
-cd ../$REPO || exit 1
-git checkout $BRANCH
+ LISP=$PWD/$LISP
+ cd ../$REPO || exit 1
+ git checkout $BRANCH
-# Run the tests. Exits with a non-zero code if there's a failure.
-$LISP -noinit -nositeinit -batch <<'EOF'
+ # Run the tests. Exits with a non-zero code if there's a failure.
+ $LISP -noinit -nositeinit -batch <<'EOF'
(require :asdf)
(push (default-directory) asdf:*central-registry*)
(asdf:test-system :trivial-package-local-nicknames)
EOF
+fi
=====================================
src/code/exports.lisp
=====================================
@@ -1213,7 +1213,8 @@
"INVALID-FASL"
"WITH-TEMPORARY-DIRECTORY"
- "WITH-TEMPORARY-FILE")
+ "WITH-TEMPORARY-FILE"
+ "DELETE-DIRECTORY")
;; gencgc features
#+gencgc
(:export "GET-GC-ASSERTIONS"
=====================================
src/code/extensions.lisp
=====================================
@@ -673,10 +673,10 @@
(defun delete-directory (dirname &key recursive)
_N"Delete the directory Dirname. If the Recursive is non-NIL,
recursively delete the directory Dirname including all files and
- subdirectories. Dirname must be a pathname to a directory. Any NAME
- or TYPE components in Dirname are ignored."
- (declare (type pathname dirname))
- (when recusive
+ subdirectories. Dirname must name a directory. Any NAME or TYPE
+ components in Dirname are ignored. A FILE-ERROR is signaled if any
+ directory cannot be deleted."
+ (when recursive
;; Find all the files or directories in DIRNAME.
(dolist (path (directory (merge-pathnames "*.*" dirname)))
;; If the path is a directory, recursively delete the directory.
@@ -685,8 +685,15 @@
(delete-directory path :recursive t)
(delete-file path))))
;; Finally delete the directory.
- (unix:unix-rmdir (namestring dirname))
- (values))
+ (multiple-value-bind (ok errno)
+ (unix:unix-rmdir (namestring dirname))
+ (unless ok
+ (error 'kernel:simple-file-error
+ :pathname dirname
+ :format-control (intl:gettext "Could not remove directory \"~A\": ~A.")
+ :format-arguments (list dirname
+ (unix:get-unix-error-msg errno))))
+ ok))
;;; WITH-TEMPORARY-DIRECTORY -- Public
=====================================
src/code/pathname.lisp
=====================================
@@ -843,13 +843,12 @@ a host-structure or string."
(flet ((check-component-validity (name name-or-type)
(when (stringp name)
(when (eq host (%pathname-host *default-pathname-defaults*))
- (when (find-if #'(lambda (c)
- ;; Illegal characters are a slash or NUL.
- (or (char= c #\/)
- (char= c #\nul)))
+ (when (some #'(lambda (c)
+ ;; Illegal characters are a slash or NUL.
+ (case c
+ ((#\/ #\null) t)))
name)
- (cerror _"Continue anyway"
- _"Pathname component ~A cannot contain a slash or nul character: ~S"
+ (error _"Pathname component ~A cannot contain a slash or nul character: ~S"
name-or-type name))))))
(check-component-validity name :pathname-name)
(check-component-validity type :pathname-type)
=====================================
src/code/unix.lisp
=====================================
@@ -2564,5 +2564,5 @@
(cast result c-call:c-string)
nil)
status))
- (free-alien name)))))
+ (free-alien result)))))
=====================================
src/compiler/float-tran-dd.lisp
=====================================
@@ -290,10 +290,10 @@
(optimize (speed 3)))
;; If the numbers are too big, scale them done so SPLIT doesn't overflow.
(multiple-value-bind (aa bb)
- (values (if (> a +two970+)
+ (values (if (> (abs a) +two970+)
(* a +two-53+)
a)
- (if (> b +two970+)
+ (if (> (abs b) +two970+)
(* b +two-53+)
b))
(let ((p (* aa bb)))
@@ -314,10 +314,10 @@
(declare (optimize (inhibit-warnings 3)))
;; If the numbers was scaled down, we need to scale the
;; result back up.
- (when (> a +two970+)
+ (when (> (abs a) +two970+)
(setf p (* p +two53+)
e (* e +two53+)))
- (when (> b +two970+)
+ (when (> (abs b) +two970+)
(setf p (* p +two53+)
e (* e +two53+)))
(values p e))))))))
=====================================
src/general-info/release-22a.md
=====================================
@@ -34,6 +34,7 @@ public domain.
* #446: Use C compiler to get errno values to update UNIX
defpackage with errno symbols
* #453: Use correct flags for analyzer and always save logs.
+ * #458: Spurious overflow in double-double-float multiply
* Other changes:
* Improvements to the PCL implementation of CLOS:
* Changes to building procedure:
=====================================
src/i18n/locale/cmucl.pot
=====================================
@@ -6011,8 +6011,13 @@ msgstr ""
msgid ""
"Delete the directory Dirname. If the Recursive is non-NIL,\n"
" recursively delete the directory Dirname including all files and\n"
-" subdirectories. Dirname must be a pathname to a directory. Any NAME\n"
-" or TYPE components in Dirname are ignored."
+" subdirectories. Dirname must name a directory. Any NAME or TYPE\n"
+" components in Dirname are ignored. A FILE-ERROR is signaled if any\n"
+" directory cannot be deleted."
+msgstr ""
+
+#: src/code/extensions.lisp
+msgid "Could not remove directory \"~A\": ~A."
msgstr ""
#: src/code/extensions.lisp
=====================================
tests/float-x86.lisp
=====================================
@@ -5,6 +5,11 @@
(in-package "FLOAT-X86-TESTS")
+;; This tests the floating-point modes for x86. This works only if we
+;; have the feature :sse2 but not :darwin since darwin has always used
+;; sse2 and not x87. But see also how FLOATING-POINT-MODES is
+;; implemented in src/code/float-trap.lisp.
+#+(and sse2 (not darwin))
(define-test set-floating-point-modes
(let ((old-x87-modes (x86::x87-floating-point-modes))
(old-sse2-modes (x86::sse2-floating-point-modes))
=====================================
tests/float.lisp
=====================================
@@ -342,3 +342,9 @@
(x86::x87-floating-point-modes)))))
(assert-true (typep new-mode 'x86::float-modes))
(assert-equal new-mode (setf (x86::x87-floating-point-modes) new-mode))))
+
+;; Issue #458
+(define-test dd-mult-overflow
+ (:tag :issues)
+ (assert-equal -2w300
+ (* -2w300 1w0)))
=====================================
tests/os.lisp
=====================================
@@ -51,6 +51,7 @@
(assert-equal 2153718000 st-atime)
(assert-equal 2153718000 st-mtime))))
+#+linux
(define-test stat.64-bit-timestamp-2106
(:tag :issues)
(let ((test-file #.(merge-pathnames "resources/64-bit-timestamp-2106.txt"
=====================================
tests/pathname.lisp
=====================================
@@ -143,6 +143,18 @@
:truenamep nil :follow-links nil)))
(assert-equal dir-tilde dir-home))))
+(define-test delete-directory
+ (:tag :issues)
+ (ext:with-temporary-directory (path)
+ (let ((dir (ensure-directories-exist (merge-pathnames "tmp/a/b/c/" path))))
+ ;; Try to delete the directory. It should fail..
+ (assert-error 'kernel:simple-file-error
+ (ext:delete-directory (merge-pathnames "tmp/" path)))
+ ;; Now recursively delete the directory.
+ (assert-true (ext:delete-directory (merge-pathnames "tmp/" path)
+ :recursive t))
+ (assert-false (directory (merge-pathnames "tmp/" path))))))
+
(define-test issue.454.illegal-pathname-chars
(:tag :issues)
;; A slash (Unix directory separater) is not allowed.
@@ -167,3 +179,4 @@
(make-pathname :name "."))
(assert-error 'simple-error
(make-pathname :name "..")))
+
=====================================
tests/unix.lisp
=====================================
@@ -88,4 +88,7 @@
(assert-false result)
(assert-true (and (integerp errno) (plusp errno)))))
-
+(define-test unix-get-username
+ (let ((uid (unix:unix-getuid)))
+ (assert-true uid)
+ (assert-true (unix::unix-get-username uid))))
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/b2c51c988f73c44824b5d9…
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/b2c51c988f73c44824b5d9…
You're receiving this email because of your account on gitlab.common-lisp.net.
1
0