Chez numeric tower: exact ints / Ratio / double for JVM parity (jolt-n6al)

jolt was all-flonum (one :number type, inherited from Janet whose only number
type is a double). The Chez runtime has a full numeric tower, so the zero-Janet
path now carries it = JVM Clojure semantics:

  (/ 1 2)      => 1/2      (exact Ratio, was 0.5)
  (integer? 3) => true   (integer? 3.0) => false   (float? 3.0) => true
  (ratio? (/ 1 2)) => true   (= 3 3.0) => false   (== 3 3.0) => true
  (+ 1 2) => 3 (exact)   (/ 1.0 2) => 0.5 (double)

jolt= was already exactness-aware (values.ss) and == is value-equality, so
=/== match the JVM split. The reader preserves exactness (integer literals exact,
a/b ratios exact rationals, decimals/exponents flonums); backend_scheme emit-const
renders exact ints/ratios and flonums faithfully; the value-position arithmetic,
count, int, compare, bit ops, parseLong, string .length/.indexOf, range,
timestamps, and array bytes return exact integers (= JVM int/long) instead of
coercing to flonum. double/parseDouble/clojure.math floor|ceil|signum stay double.

Only the zero-Janet path carries the tower (the Janet reader loses exactness into
a double before emit). The prelude/all-flonum path is unaffected for compiled code;
the runtime reader is shared, so a couple of all-flonum reader assertions become
value (==) assertions. ~16 numeric corpus cases now give the JVM tower value vs the
Janet-era :expected and are allowlisted as tower divergences (Chez == reference
JVM) pending the corpus flip to JVM (jolt-ecz0). No BigDecimal type (1M).

Re-minted. zero-janet 2682 (floor 2698->2682, the reclassified tower cases), 0 new
divergences; fixpoint 10/10, bootstrap 6/6, spine 35/35, cli 49/49; Janet gate 155
files 0 failed.
This commit is contained in:
Yogthos 2026-06-20 23:09:27 -04:00
parent eb1c3298a4
commit 467ad75ff7
20 changed files with 291 additions and 210 deletions

View file

@ -4,24 +4,25 @@
;; to an exact integer, operate, and return a flonum. parse-* match the seed's
;; strict shapes (Clojure 1.11: nil on malformed, throw on a non-string).
;; bit ops return EXACT integers (= JVM long). ->int coerces the operand.
(define (->int x) (exact (truncate x)))
(define (jolt-bit-and a b) (exact->inexact (bitwise-and (->int a) (->int b))))
(define (jolt-bit-or a b) (exact->inexact (bitwise-ior (->int a) (->int b))))
(define (jolt-bit-xor a b) (exact->inexact (bitwise-xor (->int a) (->int b))))
(define (jolt-bit-and-not a b) (exact->inexact (bitwise-and (->int a) (bitwise-not (->int b)))))
(define (jolt-bit-not a) (exact->inexact (bitwise-not (->int a))))
(define (jolt-bit-shift-left x n) (exact->inexact (bitwise-arithmetic-shift-left (->int x) (->int n))))
(define (jolt-bit-shift-right x n) (exact->inexact (bitwise-arithmetic-shift-right (->int x) (->int n))))
(define (jolt-bit-and a b) (bitwise-and (->int a) (->int b)))
(define (jolt-bit-or a b) (bitwise-ior (->int a) (->int b)))
(define (jolt-bit-xor a b) (bitwise-xor (->int a) (->int b)))
(define (jolt-bit-and-not a b) (bitwise-and (->int a) (bitwise-not (->int b))))
(define (jolt-bit-not a) (bitwise-not (->int a)))
(define (jolt-bit-shift-left x n) (bitwise-arithmetic-shift-left (->int x) (->int n)))
(define (jolt-bit-shift-right x n) (bitwise-arithmetic-shift-right (->int x) (->int n)))
(define (bit-mask n) (bitwise-arithmetic-shift-left 1 (->int n)))
(define (jolt-bit-set x n) (exact->inexact (bitwise-ior (->int x) (bit-mask n))))
(define (jolt-bit-clear x n) (exact->inexact (bitwise-and (->int x) (bitwise-not (bit-mask n)))))
(define (jolt-bit-flip x n) (exact->inexact (bitwise-xor (->int x) (bit-mask n))))
(define (jolt-bit-set x n) (bitwise-ior (->int x) (bit-mask n)))
(define (jolt-bit-clear x n) (bitwise-and (->int x) (bitwise-not (bit-mask n))))
(define (jolt-bit-flip x n) (bitwise-xor (->int x) (bit-mask n)))
(define (jolt-bit-test x n) (not (zero? (bitwise-and (->int x) (bit-mask n)))))
;; unsigned-bit-shift-right: logical shift over 64-bit longs. For the common
;; non-negative operand it equals the arithmetic shift; the negative-operand
;; 64-bit-window case is not modeled (no fixed-width longs on the all-flonum side).
;; 64-bit-window case is not modeled.
(define (jolt-unsigned-bit-shift-right x n)
(exact->inexact (bitwise-arithmetic-shift-right (->int x) (->int n))))
(bitwise-arithmetic-shift-right (->int x) (->int n)))
;; ---- string->scalar parsers -------------------------------------------------
(define (ascii-digit? c) (and (char>=? c #\0) (char<=? c #\9)))
@ -34,7 +35,7 @@
(define (jolt-parse-long s)
(if (not (string? s)) (error #f "parse-long requires a string" s)
(if (parse-long-shape? s) (exact->inexact (string->number s)) jolt-nil)))
(if (parse-long-shape? s) (string->number s) jolt-nil))) ; exact long
;; strict float shape: [+-]? ( D+ (. D*)? | . D+ ) ([eE][+-]? D+)? fully anchored.
(define (parse-double-shape? s)