The trio the author decided on 2026-09-20 is now all built: def is CL's defparameter — its initialiser runs on every daemon re-run, unguarded, so an edited initialiser repaints the same storage on C-c C-c plus re-run — defonce (Clojure's name for CL's defvar, per the author) initialises once behind the .init~once. flag, and defconst stays the image. One parse arm reads both forms; the difference is Ast.reinit, carried to Tast.global's grerun. Emit.startup_plan gives a def no guard flag, and Check.check_global lifts every def initialiser — zero and literal included — into global/<n>, so the host's startup reaches it through the function cell and a re-evaluated def swaps it (Session's def_inits; Emit.redefinition declares the cell for a non-sibling target). The old defvar spelling is refused with the rename and both compiling spellings, and every program, test, doc and editor list is swept — except sand.flan, the author's live WIP, whose seven defvar lines are flagged in FIX.org and keep its three dependent tests red on this branch.
137 lines
5.8 KiB
Plaintext
137 lines
5.8 KiB
Plaintext
;;;; The rest of libm, at both widths — what math.flan and math2.flan left out.
|
|
;;;;
|
|
;;;; The rule those two set holds here unchanged and is the only reason this
|
|
;;;; file looks the way it does: none of these is correctly rounded under
|
|
;;;; IEEE-754 except sqrt, fabs, the rounding three and fmod, so every value
|
|
;;;; below is one whose answer is exact in binary — zero, one, a power of two,
|
|
;;;; a perfect square, a perfect cube. A case that pinned glibc's last bit
|
|
;;;; would pass here and fail on wasi-libc.
|
|
;;;;
|
|
;;;; The acceptance table builds this at -O0 as well, and that run is the one
|
|
;;;; that matters: at -O2 LLVM folds a libm call over two literals and leaves
|
|
;;;; no symbol to resolve, which is how a missing -lm hid the first time.
|
|
|
|
(defn show [x f32] ()
|
|
(print x)
|
|
(print " "))
|
|
|
|
(defn show64 [x f64] ()
|
|
(print x)
|
|
(print " "))
|
|
|
|
;;; The operands of the (% ...) block at the end. See the note there for why
|
|
;;; they are globals.
|
|
(defonce rem-a f64 7.5)
|
|
(defonce rem-na f64 -7.5)
|
|
(defonce rem-b f64 2.0)
|
|
(defonce rem-nb f64 -2.0)
|
|
(defonce rem-z f64 0.0)
|
|
(defonce rem-huge f64 1e300)
|
|
(defonce rem-a32 f32 7.5)
|
|
(defonce rem-na32 f32 -7.5)
|
|
(defonce rem-b32 f32 2.0)
|
|
(defonce rem-z32 f32 0.0)
|
|
|
|
(defn main [] i32
|
|
;; The f32 half. tan, the three inverses, the three logarithms and exp.
|
|
(show (tan-f32 0.0)) ; 0
|
|
(show (asin-f32 0.0)) ; 0
|
|
(show (acos-f32 1.0)) ; 0
|
|
(show (atan-f32 0.0)) ; 0
|
|
(show (log-f32 1.0)) ; 0
|
|
(show (log2-f32 8.0)) ; 3
|
|
(show (log10-f32 1000.0)) ; 3
|
|
(show (exp-f32 0.0)) ; 1
|
|
(println "")
|
|
|
|
(show (fmod-f32 7.0 4.0)) ; 3
|
|
;; The sign follows the dividend and not the divisor, which is the line a
|
|
;; caller reaching for a modulo gets wrong. Written down as a case.
|
|
(show (fmod-f32 -1.0 3.0)) ; -1
|
|
(show (hypot-f32 3.0 4.0)) ; 5
|
|
(show (cbrt-f32 27.0)) ; 3
|
|
;; The negative root, where (pow-f32 x 0.33333334) would be NaN: pow goes
|
|
;; through a logarithm and cbrt does not.
|
|
(show (cbrt-f32 -8.0)) ; -2
|
|
(show (abs-f32 -2.5)) ; 2.5
|
|
(println "")
|
|
|
|
;; The f64 half, at the same exact values. This block is the whole point of
|
|
;; the f64 face existing: before it, every one of these was a cast down to
|
|
;; f32 and back, and the cast down is where the precision went.
|
|
(show64 (sqrt-f64 16.0)) ; 4
|
|
(show64 (sin-f64 0.0)) ; 0
|
|
(show64 (cos-f64 0.0)) ; 1
|
|
(show64 (tan-f64 0.0)) ; 0
|
|
(show64 (asin-f64 0.0)) ; 0
|
|
(show64 (acos-f64 1.0)) ; 0
|
|
(show64 (atan-f64 0.0)) ; 0
|
|
(show64 (atan2-f64 0.0 1.0)) ; 0
|
|
(println "")
|
|
|
|
(show64 (log-f64 1.0)) ; 0
|
|
(show64 (log2-f64 1024.0)) ; 10
|
|
(show64 (log10-f64 100.0)) ; 2
|
|
(show64 (exp-f64 0.0)) ; 1
|
|
(show64 (pow-f64 2.0 10.0)) ; 1024
|
|
(show64 (fmod-f64 7.0 4.0)) ; 3
|
|
(show64 (hypot-f64 3.0 4.0)) ; 5
|
|
(show64 (cbrt-f64 8.0)) ; 2
|
|
(show64 (abs-f64 -1.5)) ; 1.5
|
|
(println "")
|
|
|
|
;; The f64 rounding family, which is libm's where the f32 one is Flan's —
|
|
;; and it agrees with the Flan one where they overlap: half away from zero,
|
|
;; so -2.5 goes to -3 and not to -2.
|
|
(show64 (floor-f64 -2.5)) ; -3
|
|
(show64 (ceil-f64 -2.5)) ; -2
|
|
(show64 (round-f64 -2.5)) ; -3
|
|
(show64 (round-f64 2.5)) ; 3
|
|
(println "")
|
|
|
|
;; Integer magnitude, one generic under integer? — the per-width pair
|
|
;; collapsed into it (FIX.org 2026-09-20). Two widths, two copies.
|
|
(print (abs -7)) (print " ") ; 7
|
|
(print (abs (i64 -7))) (print " ") ; 7
|
|
(print (abs 7)) (print " ") ; 7
|
|
;; tau is 2pi at both widths. Pinning the relation rather than the digits is
|
|
;; what catches a constant written to too few of them.
|
|
(print (= tau-f32 (* 2.0 pi-f32))) (print " ")
|
|
(print (= tau-f64 (* 2.0 pi-f64)))
|
|
(println "")
|
|
|
|
;; pi is the one value here that can be pinned without pinning a libm: it is
|
|
;; a literal the compiler rounds, so it is the same on every target.
|
|
(println (and (> pi-f64 3.14159265) (< pi-f64 3.14159266)))
|
|
|
|
;; The operator spelling of the same function. A typed float (% x y) is
|
|
;; fmod: LLVM lowers it to `frem`, which at -O0 becomes a call to fmod or
|
|
;; fmodf, and the x86 backend calls the same two symbols. Everything here
|
|
;; comes through a global rather than a literal for the reason arith.flan
|
|
;; gives — a literal pair is folded before either backend sees an operator,
|
|
;; and the folded answer would be evidence about the constant folder and
|
|
;; not about the lowering.
|
|
;;
|
|
;; Six values on the first line: the four sign combinations, where a modulo
|
|
;; written in place of a remainder disagrees — the sign follows the dividend
|
|
;; — and then the f32 pair, asking fmodf the same. The second line is the
|
|
;; two answers IEEE defines where an integer % would have died: a zero
|
|
;; divisor and a NaN dividend are both NaN, not a signal.
|
|
(show64 (% rem-a rem-b)) ; 1.5
|
|
(show64 (% rem-na rem-b)) ; -1.5
|
|
(show64 (% rem-a rem-nb)) ; 1.5
|
|
(show64 (% rem-na rem-nb)) ; -1.5
|
|
(show (% rem-a32 rem-b32)) ; 1.5
|
|
(show (% rem-na32 rem-b32)) ; -1.5
|
|
(println "")
|
|
|
|
(show64 (% rem-a rem-z)) ; nan
|
|
(show64 (% (/ rem-z rem-z) rem-b)) ; nan
|
|
(show (% rem-a32 rem-z32)) ; nan
|
|
;; A quotient past anything an integer could name, which is where a
|
|
;; remainder written as x - trunc(x/y)*y has nothing left to truncate.
|
|
(show64 (% rem-huge rem-b)) ; 0
|
|
(show64 (% rem-b rem-huge)) ; 2
|
|
(println "")
|
|
0)
|