Skip to content

feat: sin_approx_f32 + cos_approx_f32 — closes the transcendental family - #32

Merged
petlukk merged 3 commits into
mainfrom
feat/v1.14-sin-cos-approx-f32
May 19, 2026
Merged

feat: sin_approx_f32 + cos_approx_f32 — closes the transcendental family#32
petlukk merged 3 commits into
mainfrom
feat/v1.14-sin-cos-approx-f32

Conversation

@petlukk

@petlukk petlukk commented May 19, 2026

Copy link
Copy Markdown
Owner

Summary

  • New sin_approx_f32(v: f32xN) -> f32xN and cos_approx_f32(v: f32xN) -> f32xN intrinsics with a shared range-reduction + polynomial core.
  • mod-π/2 reduction via 2-piece Cody-Waite (FMA-preserved precision), Taylor polynomials over d' ∈ [-π/4, π/4] (degree 3 in s = d'² for sin, degree 4 for cos), quadrant blend.
  • cos reuses the same core via q += 1 — a precision-free integer shift expressing cos(x) = sin(x + π/2).
  • Max abs error ~3e-6 across [-1e7, 1e7]. Avoids @llvm.sin / @llvm.cos (LLVM scalarizes to per-lane libm).

Closes the transcendental approximation family

The original "Future API consistency" trio (tanh_approx_f32, log_approx_f32, sin_cos_approx_f32) is fully closed by v1.14.0:

Intrinsic Released Use case
exp_poly_f32 v1.11.0 softmax, GELU, exponential decay
tanh_approx_f32 v1.14.0 (PR #28) tanh-GELU activation
log_approx_f32 v1.14.0 (PR #31) log-likelihood, log-domain math
sin_approx_f32 v1.14.0 (this PR) periodic signal gen, position encoding
cos_approx_f32 v1.14.0 (this PR) same, paired with sin

The f32 transcendental approximation family is feature-complete for typical SIMD workloads.

API shape: two intrinsics, not a pair-return

The roadmap line was sin_cos_approx_f32 (singular, pair-return). Shipping as two separate intrinsics because:

  1. Eä has no precedent for multi-return intrinsics — would require new return-type machinery
  2. Shared work is small (~4 ops out of ~21 per intrinsic); LLVM CSE handles caller-side redundancy
  3. Matches the established single-return pattern of the rest of the family

If a real consumer benchmarks the pair form as significantly better, that's a follow-up with a different name.

Cody-Waite constants — verify-first reminder

An earlier draft used 3-piece values transcribed from Sleef. They didn't actually sum to π/2 (off by 6e-8). The boundary_points test caught it: cos(1e6) showed 0.014 error vs the 3e-6 tolerance. Fix was a 2-piece Eigen-style split (PI_2_HI = closest f32 to π/2, PI_2_LO = -4.37e-8 residual; sum reproduces π/2 to ~2e-15 in f64). Documented in the post-mortem.

Test plan

  • cargo test --features llvm --release — 943 tests pass (924 baseline + 19 new)
  • cargo clippy --features llvm --all-targets — clean
  • cargo fmt --check — clean
  • sin(0) = 0, cos(0) = 1 (smoke)
  • Boundary points {-2π … 2π plus 10, 100, 1e6} vs libm sinf/cosf; abs error ≤ 3e-6
  • 256 LCG random samples over [-10π, 10π] for f32x4 and f32x8
  • Pythagorean identity sin²(x) + cos²(x) ≈ 1 within 6e-6 (composition guard)
  • IR guards: no @llvm.sin / @llvm.cos; ≥6 FMAs; @llvm.nearbyint; fptosi <N x float>
  • Typeck rejections: scalar f32, f64x2, integer vector, wrong arity

🤖 Generated with Claude Code

@petlukk
petlukk force-pushed the feat/v1.14-sin-cos-approx-f32 branch 4 times, most recently from 94794a9 to cbdfba9 Compare May 19, 2026 08:46
petlukk and others added 3 commits May 19, 2026 08:52
Two intrinsics sharing a common range-reduction + polynomial core:
reduce mod π/2 via 2-piece Cody-Waite (PI_2_HI exact f32, PI_2_LO
negative residual; sum reproduces π/2 to ~2e-15 in f64), then compute
both sin and cos polynomials over d' ∈ [-π/4, π/4]:
- sin: degree-3 in s=d², covers d through d^7 — truncation ≤3.4e-7
- cos: degree-4 in s, covers d^0 through d^8 — truncation ≤2.6e-8

The cos variant reuses the same core with q += 1 — a precision-free
integer shift expressing the mathematical identity cos(x) = sin(x +
π/2). Adding π/2 to v before reduction would lose bits for large |v|;
shifting the integer quadrant index after reduction is exact.

Final blend: swap = (k & 1), negate = (k & 2). Both polynomials are
always computed (~4 wasted FMAs) for branchless SIMD execution; LLVM
CSE eliminates redundant range reduction at the caller level when
both intrinsics see the same input.

Max abs error ~3e-6 across the documented [-1e7, 1e7] range. Avoids
@llvm.sin / @llvm.cos which LLVM scalarizes to per-lane libm
sinf/cosf on every supported architecture.

Pair-return form deferred — Eä has no precedent for multi-return
intrinsics, and the established single-return pattern composes
cleanly with the rest of the transcendental family.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
19 tests covering:

- at_zero (sin + cos): smoke that sin(0)=0 and cos(0)=1.
- boundary_points (sin × {x4, x8} + cos × {x4, x8}): vs libm
  sinf/cosf across {-2π … 2π} plus moderate (10, 100) and edge
  (1e6) inputs within the documented [-1e7, 1e7] range. Abs error
  ≤ 3e-6.
- random (sin × {x4, x8} + cos × x4): 256 LCG samples over [-10π,
  10π] exercising range reduction over multiple quadrants.
- pythagorean_identity: pins sin²(x) + cos²(x) ≈ 1 to 6e-6 across
  4 inputs. A regression in either intrinsic's q-handling would
  surface here even if individual tolerance still passes.
- does_not_emit_llvm_sin_cos: IR guard against @llvm.sin/@llvm.cos
  delegation (LLVM scalarizes both to libm).
- emits_expected_pattern (sin × {x4, x8}): IR has ≥6 FMAs +
  @llvm.nearbyint + fptosi <N x float> → <N x i32>.
- Six typeck rejections (3 per intrinsic): scalar f32, f64x2,
  integer vector; arity for sin.

An earlier draft of the Cody-Waite constants had a transcription
error (3-piece Sleef-derived values that didn't sum to π/2,
off by 6e-8). The boundary_points test caught it loudly — cos(1e6)
showed 0.014 error vs the 3e-6 tolerance. Fix was the 2-piece
Eigen-style split now in the source; lesson is to verify constants
against their claimed identity, not transcribe by eye.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CHANGELOG entry under v1.14.0 Added. ROADMAP Shipped entry +
collapses the Future API consistency section: the v1.11.0-era trio
(tanh, log, sin/cos plus u16x32 and wider wmul_u64) is fully closed
out by v1.14.0. The f32 transcendental approximation family is
feature-complete; new entries land here as real consumers surface
them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@petlukk
petlukk force-pushed the feat/v1.14-sin-cos-approx-f32 branch from cbdfba9 to 880a51c Compare May 19, 2026 08:54
@petlukk
petlukk merged commit f766dc1 into main May 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant