;; numerics.sx — Phase 10 numeric tower verification. ;; ;; Practical integer-precision limit in Haskell-on-SX: ;; • Raw SX `(* a b)` stays exact up to ±2^62 (≈ 4.6e18, OCaml int63). ;; • BUT the Haskell tokenizer/parser parses an integer literal as a float ;; once it exceeds 2^53 (≈ 9.007e15). Once any operand is a float, the ;; binop result is a float (and decimal-precision is lost past 2^53). ;; • Therefore: programs that stay below ~9e15 are exact; larger literals ;; or accumulated products silently become floats. `factorial 18` is the ;; last factorial that stays exact (6.4e15); `factorial 19` already floats. ;; ;; In Haskell terms, `Int` and `Integer` both currently map to SX number, so ;; we don't yet support arbitrary-precision Integer. Documented; unbounded ;; Integer is out of scope for Phase 10 — see Phase 11+ if it becomes needed. (define hk-as-list (fn (xs) (cond ((and (list? xs) (= (first xs) "[]")) (list)) ((and (list? xs) (= (first xs) ":")) (cons (nth xs 1) (hk-as-list (nth xs 2)))) (:else xs)))) (hk-test "factorial 10 = 3628800 (small, exact)" (hk-deep-force (hk-run "fact 0 = 1\nfact n = n * fact (n - 1)\nmain = fact 10")) 3628800) (hk-test "factorial 15 = 1307674368000 (mid-range, exact)" (hk-deep-force (hk-run "fact 0 = 1\nfact n = n * fact (n - 1)\nmain = fact 15")) 1307674368000) (hk-test "factorial 18 = 6402373705728000 (last exact factorial)" (hk-deep-force (hk-run "fact 0 = 1\nfact n = n * fact (n - 1)\nmain = fact 18")) 6402373705728000) (hk-test "1000000 * 1000000 = 10^12 (exact)" (hk-deep-force (hk-run "main = 1000000 * 1000000")) 1000000000000) (hk-test "1000000000 * 1000000000 = 10^18 (exact, at boundary)" (hk-deep-force (hk-run "main = 1000000000 * 1000000000")) 1e+18) (hk-test "2^62 boundary: pow accumulates exactly" (hk-deep-force (hk-run "pow b 0 = 1\npow b n = b * pow b (n - 1)\nmain = pow 2 62")) 4.6116860184273879e+18) (hk-test "show factorial 12 = 479001600 (whole, fits in 32-bit)" (hk-deep-force (hk-run "fact 0 = 1\nfact n = n * fact (n - 1)\nmain = show (fact 12)")) "479001600") (hk-test "negate large positive — preserves magnitude" (hk-deep-force (hk-run "main = negate 1000000000000000000")) -1e+18) (hk-test "abs negative large — preserves magnitude" (hk-deep-force (hk-run "main = abs (negate 1000000000000000000)")) 1e+18) (hk-test "div on large ints" (hk-deep-force (hk-run "main = div 1000000000000000000 1000000000")) 1000000000) (hk-test "fromIntegral 42 = 42 (identity in our runtime)" (hk-deep-force (hk-run "main = fromIntegral 42")) 42) (hk-test "fromIntegral preserves negative" (hk-deep-force (hk-run "main = fromIntegral (negate 7)")) -7) (hk-test "fromIntegral round-trips through arithmetic" (hk-deep-force (hk-run "main = fromIntegral 5 + fromIntegral 3")) 8) (hk-test "fromIntegral in a program (mixing with map)" (hk-as-list (hk-deep-force (hk-run "main = map fromIntegral [1,2,3]"))) (list 1 2 3)) (hk-test "toInteger 100 = 100 (identity)" (hk-deep-force (hk-run "main = toInteger 100")) 100) (hk-test "fromInteger 7 = 7 (identity)" (hk-deep-force (hk-run "main = fromInteger 7")) 7) (hk-test "toInteger / fromInteger round-trip" (hk-deep-force (hk-run "main = fromInteger (toInteger 42)")) 42) (hk-test "toInteger preserves negative" (hk-deep-force (hk-run "main = toInteger (negate 13)")) -13) (hk-test "show 3.14 = 3.14" (hk-deep-force (hk-run "main = show 3.14")) "3.14") (hk-test "show 1.0e10 — whole-valued float renders as decimal (int/float ambiguity)" (hk-deep-force (hk-run "main = show 1.0e10")) "10000000000") (hk-test "show 0.001 uses scientific form (sub-0.1)" (hk-deep-force (hk-run "main = show 0.001")) "1.0e-3") (hk-test "show negative float" (hk-deep-force (hk-run "main = show (negate 3.14)")) "-3.14") {:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}