The fifth predicate: integer? admits every integer kind and no float, entails numeric? (and through it ordered? and equal?), and gates what only integers support — the bitwise fold asks for it, the shifts admit a bounded variable under it, and the float literal in an integer? body is refused in the bound's own words. The literal arm needed nothing: the entailment admits an integer constant under either bound. abs-i32 and abs-i64 collapse into one integer?-bounded generic whose i32/i64 copies even keep the old symbols; abs-f32/abs-f64 stay as the float spellings because the right float abs is a sign-bit clear no integer body spells, and (abs 1.5) now refuses naming the bound — the where clause is checked before the name-collision check, which used to answer that call with 'abs-f64 is already defined'. Mixed widths at one $t join at the wider type now, in either argument order — the author reversed the refuse-both rule on 2026-09-20. A joinless pair is deferred and re-asked against the final binding, so a later wider argument settles u32-vs-i32; u64-vs-i64 still refuses, and a container-bound variable still binds exactly. The out-widened arguments catch up through the ordinary Cast. Two review follow-ups folded in: a struct field's unknown-lowercase message stops suggesting a parameter vector it does not have, and the tyvar-at-dyn message says defgeneric/defmethod in words instead of a schematic that does not compile.
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.
|
|
(defvar rem-a f64 7.5)
|
|
(defvar rem-na f64 -7.5)
|
|
(defvar rem-b f64 2.0)
|
|
(defvar rem-nb f64 -2.0)
|
|
(defvar rem-z f64 0.0)
|
|
(defvar rem-huge f64 1e300)
|
|
(defvar rem-a32 f32 7.5)
|
|
(defvar rem-na32 f32 -7.5)
|
|
(defvar rem-b32 f32 2.0)
|
|
(defvar 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)
|