Unchecked / *unchecked-math* arithmetic wraps to signed 64-bit
clojure.core's unchecked-* (and +/-/*/inc/dec under *unchecked-math*) are long
ops that WRAP on overflow; jolt's checked arithmetic is arbitrary-precision and
its unchecked-* were plain non-wrapping (+ x y), diverging from the JVM. Now they
truncate to the low 64 bits as a signed long, matching Clojure:
(unchecked-add 9223372036854775807 1) => -9223372036854775808
(unchecked-multiply 9223372036854775807 …) => 1
- host/chez/seq.ss: jolt-wrap64 + binary jolt-unc{add,sub,mul,inc,dec,neg}2 and
the variadic clojure.core/unchecked-* fns (def-var!'d in natives-seq.ss, where
def-var! is bound). The overlay's plain unchecked-* defns are removed.
- backend lng-ops: unchecked-+/-/* emit the wrapping jolt-unc* helpers (the
raising fx ops can't wrap on Chez's 61-bit fixnums); unchecked-inc/dec too.
- *unchecked-math* is honored: the analyzer reads it (jolt.host/unchecked-math?)
and rewrites +/-/*/inc/dec to their unchecked-* for the rest of a file that
(set!)s it, like the JVM.
- jolt->fx: a ^long value that overflows the 61-bit fixnum range passes through
as an exact integer instead of erroring (a full-width long from wrapping math).
Also adds Long/bitCount / numberOfLeadingZeros / reverse and Math/getExponent /
scalb (test.check's splittable PRNG uses them).
This lets clojure.test.check load and run quick-check on jolt. re-mint (analyzer/
backend/overlay are seed sources). make test green (+6 corpus rows, 0 new
divergences, numeric gate updated), shakesmoke byte-identical.
This commit is contained in:
parent
86dd9650b6
commit
a028cab04f
12 changed files with 588 additions and 517 deletions
|
|
@ -152,6 +152,34 @@
|
|||
(define (jolt-min . xs) (apply min xs))
|
||||
(define (jolt-max . xs) (apply max xs))
|
||||
|
||||
;; --- unchecked (Java long) arithmetic: wrap to signed 64 bits ----------------
|
||||
;; Clojure's unchecked-* (and +/-/* under *unchecked-math*) are long ops that
|
||||
;; WRAP on overflow; jolt's checked arithmetic is arbitrary-precision. These
|
||||
;; truncate to the low 64 bits as a two's-complement signed long. Chez fixnums are
|
||||
;; 61-bit, so wrapping uses bignum bit ops + a mask (no fx fast path). The backend
|
||||
;; emits the binary jolt-unc* for :long-typed unchecked ops; the variadic
|
||||
;; clojure.core/unchecked-* fns reduce through them.
|
||||
(define unc-mask64 #xFFFFFFFFFFFFFFFF)
|
||||
(define unc-2^63 #x8000000000000000)
|
||||
(define unc-2^64 #x10000000000000000)
|
||||
(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)))
|
||||
(define (jolt-uncadd2 a b) (jolt-wrap64 (+ a b)))
|
||||
(define (jolt-uncsub2 a b) (jolt-wrap64 (- a b)))
|
||||
(define (jolt-uncmul2 a b) (jolt-wrap64 (* a b)))
|
||||
(define (jolt-uncinc x) (jolt-wrap64 (+ x 1)))
|
||||
(define (jolt-uncdec x) (jolt-wrap64 (- x 1)))
|
||||
(define (jolt-uncneg x) (jolt-wrap64 (- 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)
|
||||
(cond ((null? xs) 0) ((null? (cdr xs)) (jolt-uncneg (car xs))) (else (fold-left jolt-uncsub2 (car xs) (cdr xs)))))
|
||||
(define (jolt-unchecked-div a b) (quotient (jolt-wrap64 a) (jolt-wrap64 b)))
|
||||
(define (jolt-unchecked-rem a b) (remainder (jolt-wrap64 a) (jolt-wrap64 b)))
|
||||
;; the clojure.core/unchecked-* vars are def-var!'d in natives-seq.ss (def-var! is
|
||||
;; defined after this file loads).
|
||||
|
||||
;; ============================================================================
|
||||
;; IFn dispatch — the dynamic "value as fn" fallback. A callee that the emitter
|
||||
;; can't statically resolve to a procedure (a keyword/coll/proc held in a local)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue