flan/test/programs/math3.flan
Joseph Ferano f71cc40bb5 integer? bounds the integer-only bodies, and abs is one generic
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.
2026-09-20 21:42:50 +07:00

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)