From 253d64b1e708ec19a58f50fd78d64e9ea44d4033 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Sat, 27 Jun 2026 22:49:42 -0400 Subject: [PATCH 1/2] unchecked-* on a ratio (or any non-long) shouldn't wrap to 64-bit jolt-unc{add,sub,mul,inc,dec,neg}2 wrapped every non-flonum result to a signed 64-bit integer, so (unchecked-add 2/3 2/3) truncated to 1 instead of 4/3. Under *unchecked-math* the analyzer rewrites +/-/* to unchecked-*, so any ratio arithmetic in such a file silently floored. Clojure's unchecked-add falls back to regular arithmetic for non-primitives; only long math wraps. Wrap iff both operands are exact integers. Shaken out by test.check's gen/ratio monoid property (the + and 0 monoid held for small-integers but failed for ratios). --- host/chez/seq.ss | 24 ++++++++++++++---------- test/chez/corpus.edn | 5 +++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/host/chez/seq.ss b/host/chez/seq.ss index 96e3899..58ca46b 100644 --- a/host/chez/seq.ss +++ b/host/chez/seq.ss @@ -165,16 +165,20 @@ (define (jolt-wrap64 x) (let ((m (bitwise-and (if (and (number? x) (exact? x) (integer? x)) x (exact (floor x))) unc-mask64))) (if (>= m unc-2^63) (- m unc-2^64) m))) -;; unchecked-* only WRAP integer (long) math; on a flonum operand they are an -;; ordinary float op, since *unchecked-math* never wraps doubles — Clojure: -;; (unchecked-multiply 1.5 2.0) => 3.0, not a truncated long. (test.check's -;; rand-double is (* double-unit shifted) under *unchecked-math*.) -(define (jolt-uncadd2 a b) (if (or (flonum? a) (flonum? b)) (+ a b) (jolt-wrap64 (+ a b)))) -(define (jolt-uncsub2 a b) (if (or (flonum? a) (flonum? b)) (- a b) (jolt-wrap64 (- a b)))) -(define (jolt-uncmul2 a b) (if (or (flonum? a) (flonum? b)) (* a b) (jolt-wrap64 (* a b)))) -(define (jolt-uncinc x) (if (flonum? x) (+ x 1.0) (jolt-wrap64 (+ x 1)))) -(define (jolt-uncdec x) (if (flonum? x) (- x 1.0) (jolt-wrap64 (- x 1)))) -(define (jolt-uncneg x) (if (flonum? x) (- x) (jolt-wrap64 (- x)))) +;; unchecked-* only WRAP integer (long) math; on a flonum OR ratio operand they +;; are an ordinary numeric op, since *unchecked-math* never wraps a non-long — +;; Clojure's unchecked-add falls back to regular arithmetic for non-primitives: +;; (unchecked-multiply 1.5 2.0) => 3.0, (unchecked-add 2/3 2/3) => 4/3, not a +;; truncated long. (test.check's rand-double is (* double-unit shifted), and +;; gen/ratio sums ratios, both under *unchecked-math*.) Wrap iff both are exact +;; integers. +(define (unc-int? x) (and (exact? x) (integer? x))) +(define (jolt-uncadd2 a b) (if (and (unc-int? a) (unc-int? b)) (jolt-wrap64 (+ a b)) (+ a b))) +(define (jolt-uncsub2 a b) (if (and (unc-int? a) (unc-int? b)) (jolt-wrap64 (- a b)) (- a b))) +(define (jolt-uncmul2 a b) (if (and (unc-int? a) (unc-int? b)) (jolt-wrap64 (* a b)) (* a b))) +(define (jolt-uncinc x) (if (unc-int? x) (jolt-wrap64 (+ x 1)) (+ x 1))) +(define (jolt-uncdec x) (if (unc-int? x) (jolt-wrap64 (- x 1)) (- x 1))) +(define (jolt-uncneg x) (if (unc-int? x) (jolt-wrap64 (- x)) (- x))) (define (jolt-unchecked-add . xs) (if (null? xs) 0 (fold-left jolt-uncadd2 (car xs) (cdr xs)))) (define (jolt-unchecked-mul . xs) (if (null? xs) 1 (fold-left jolt-uncmul2 (car xs) (cdr xs)))) (define (jolt-unchecked-sub . xs) diff --git a/test/chez/corpus.edn b/test/chez/corpus.edn index c1524b6..0616755 100644 --- a/test/chez/corpus.edn +++ b/test/chez/corpus.edn @@ -1484,6 +1484,11 @@ {:suite "numbers / promoting & unchecked aliases" :label "unchecked-remainder-int negative" :expected "-1" :actual "(unchecked-remainder-int -7 2)"} {:suite "numbers / promoting & unchecked aliases" :label "unchecked-int truncates" :expected "3" :actual "(unchecked-int 3.7)"} {:suite "numbers / promoting & unchecked aliases" :label "unchecked-int negative" :expected "-3" :actual "(unchecked-int -3.7)"} + {:suite "numbers / promoting & unchecked aliases" :label "unchecked-add on ratios does not wrap to long" :expected "4/3" :actual "(unchecked-add 2/3 2/3)"} + {:suite "numbers / promoting & unchecked aliases" :label "unchecked-multiply ratio by int" :expected "4/3" :actual "(unchecked-multiply 2/3 2)"} + {:suite "numbers / promoting & unchecked aliases" :label "unchecked-negate ratio" :expected "-2/3" :actual "(unchecked-negate 2/3)"} + {:suite "numbers / promoting & unchecked aliases" :label "unchecked-add ratios summing to zero" :expected "0" :actual "(unchecked-add 1/2 -1/2)"} + {:suite "numbers / promoting & unchecked aliases" :label "unchecked-inc ratio" :expected "3/2" :actual "(unchecked-inc 1/2)"} {:suite "numbers / promoting & unchecked aliases" :label "unchecked-long" :expected "3" :actual "(unchecked-long 3.7)"} {:suite "numbers / promoting & unchecked aliases" :label "int? on integer" :expected "true" :actual "(int? 5)"} {:suite "numbers / promoting & unchecked aliases" :label "int? on double" :expected "false" :actual "(int? 5.5)"} From a49ca3b5ea92c8f99e104c6dbf39791e0c043d71 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Sat, 27 Jun 2026 23:12:16 -0400 Subject: [PATCH 2/2] jolt-wrap64 fast path: skip the mask when already in signed-64 range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chez fixnums are 61-bit, so the bignum bitwise-and mask allocates for any value past 2^60 — and unchecked-* ran it on every result, even small in-range ones. An exact integer already in [-2^63, 2^63) is its own wrap, so return it directly; only an out-of-range result (a multiply overflowing into 128 bits) needs the mask. ~30% on in-range unchecked-add loops, neutral on full-64-bit multiply. Note: the 64-bit arithmetic floor on Chez stays ~31ns/multiply (bignum, no native 64-bit int); the test.check distribution hangs are dominated by generator/dispatch overhead, not arithmetic — this is a general win for long-heavy code, not a fix for those. --- host/chez/seq.ss | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/host/chez/seq.ss b/host/chez/seq.ss index 58ca46b..b924cc2 100644 --- a/host/chez/seq.ss +++ b/host/chez/seq.ss @@ -162,9 +162,16 @@ (define unc-mask64 #xFFFFFFFFFFFFFFFF) (define unc-2^63 #x8000000000000000) (define unc-2^64 #x10000000000000000) +(define unc-neg-2^63 (- unc-2^63)) +;; Wrap to a signed 64-bit value. Fast path: an exact integer already in +;; [-2^63, 2^63) is its own wrap — skip the bignum mask, which on Chez (61-bit +;; fixnums) allocates for any value past 2^60. Only an out-of-range result (a +;; multiply overflowing into 128 bits) needs the mask + sign fixup. (define (jolt-wrap64 x) - (let ((m (bitwise-and (if (and (number? x) (exact? x) (integer? x)) x (exact (floor x))) unc-mask64))) - (if (>= m unc-2^63) (- m unc-2^64) m))) + (if (and (exact? x) (integer? x) (>= x unc-neg-2^63) (< x unc-2^63)) + x + (let ((m (bitwise-and (if (and (number? x) (exact? x) (integer? x)) x (exact (floor x))) unc-mask64))) + (if (>= m unc-2^63) (- m unc-2^64) m)))) ;; unchecked-* only WRAP integer (long) math; on a flonum OR ratio operand they ;; are an ordinary numeric op, since *unchecked-math* never wraps a non-long — ;; Clojure's unchecked-add falls back to regular arithmetic for non-primitives: