feat: sin_approx_f32 + cos_approx_f32 — closes the transcendental family - #32
Merged
Conversation
petlukk
force-pushed
the
feat/v1.14-sin-cos-approx-f32
branch
4 times, most recently
from
May 19, 2026 08:46
94794a9 to
cbdfba9
Compare
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
force-pushed
the
feat/v1.14-sin-cos-approx-f32
branch
from
May 19, 2026 08:54
cbdfba9 to
880a51c
Compare
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
sin_approx_f32(v: f32xN) -> f32xNandcos_approx_f32(v: f32xN) -> f32xNintrinsics with a shared range-reduction + polynomial core.d' ∈ [-π/4, π/4](degree 3 ins = d'²for sin, degree 4 for cos), quadrant blend.cosreuses the same core viaq += 1— a precision-free integer shift expressingcos(x) = sin(x + π/2).[-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:exp_poly_f32tanh_approx_f32log_approx_f32sin_approx_f32cos_approx_f32The 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: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_pointstest 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— cleancargo fmt --check— cleansin(0) = 0,cos(0) = 1(smoke)sinf/cosf; abs error ≤ 3e-6[-10π, 10π]for f32x4 and f32x8sin²(x) + cos²(x) ≈ 1within 6e-6 (composition guard)@llvm.sin/@llvm.cos; ≥6 FMAs;@llvm.nearbyint;fptosi <N x float>🤖 Generated with Claude Code