feat(stdlib): Math.exp/log/pow + Ease.elastic transcendentals (issue #25)
Some checks are pending
docs / build-and-deploy (push) Waiting to run

Finish the unblocked "Math / Ease" half of #25: the fixed-point
transcendentals deferred from #2. Vec.* stays blocked on the vec2 type
in #1.

- Math.exp, Math.log (natural), Math.pow — deterministic Q16.16 via two
  new prelude fns in emit_math_prelude: @fn_fx_exp2 (range-reduced 5th-order
  Taylor 2^f, then a clamped shift by the integer part) and @fn_fx_log2
  (llvm.ctlz for the exponent + an atanh series on (m-1)/(m+1) for the
  mantissa). exp=2^(x·log2 e), log=log2(x)·ln2, pow=2^(b·log2 a).
- Ease.elastic — ease-out elastic 2^(-10t)·sin((10t-0.75)·2pi/3)+1.
- Pure integer IR, so bit-identical on every platform. Results must fit the
  Q16.16 range (|x| < 32768); larger magnitudes saturate (documented).

Test selfhost/tests/transcend.ludic (registered in the self-host suite) +
docs for all four. Reseeded; the C-free bootstrap fixpoint holds. All suites
green (24 self-host, 45 regression, 29 tool).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-30 01:19:44 +03:00
parent 2002e977d9
commit 6e6467b515
9 changed files with 9278 additions and 8791 deletions

View file

@ -4,7 +4,7 @@
fn is_ease_ns(meth: ptr) -> bool {
if (meth == "in") or (meth == "out") or (meth == "in_out") { return true }
if (meth == "back") or (meth == "bounce") { return true }
if (meth == "back") or (meth == "bounce") or (meth == "elastic") { return true }
return false
}
@ -37,6 +37,17 @@ fn emit_ease_ns(meth: ptr, e: Node) -> Val {
let b = fx_mul_code(t2, "111515") # 1.70158 * t^2
return val(emit_bind(`sub i32 {a}, {b}`), "fixed")
}
if (meth == "elastic") { # ease-out elastic: springy overshoot that settles
g_uses_mathrt = true # 2^(-10t) * sin((10t - 0.75) * 2pi/3) + 1
let tt = emit_bind(`mul i32 {t.code}, 10`) # 10t
let ntt = emit_bind(`sub i32 0, {tt}`) # -10t (exp2 exponent, Q16.16)
let decay = emit_bind(`call i32 @fn_fx_exp2(i32 {ntt})`)
let ph = emit_bind(`sub i32 {tt}, 49152`) # 10t - 0.75
let ang = fx_mul_code(ph, "137258") # * (2pi/3), 2pi/3 = 137258 fixed
let s = emit_bind(`call i32 @fn_fx_sin(i32 {ang})`)
let osc = fx_mul_code(decay, s)
return val(emit_bind(`add i32 {osc}, 65536`), "fixed")
}
# ease-out bounce: four parabolic segments, selected by t (all computed, then
# picked branch-free). Shifts/offsets are the standard 2.75-denominator set.
let sA = ease_bounce_seg(t.code)