Skip to content

feat: log_approx_f32 polynomial natural log intrinsic - #31

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

feat: log_approx_f32 polynomial natural log intrinsic#31
petlukk merged 3 commits into
mainfrom
feat/v1.14-log-approx-f32

Conversation

@petlukk

@petlukk petlukk commented May 19, 2026

Copy link
Copy Markdown
Owner

Summary

  • New log_approx_f32(v: f32xN) -> f32xN intrinsic — Eigen/Cephes-family polynomial natural log. Bit-level decomposition of x = m · 2^e (frexp convention, m ∈ [0.5, 1)), √2/2 rebalance, degree-8 Horner in (m - 1), Cody-Waite recombine with e · ln(2).
  • Max abs error ~3e-6 across (0, +∞). Composes cleanly with exp_poly_f32 — pin-tested via a roundtrip kernel at ~1e-4 relative.
  • Avoids @llvm.log.v*f32, which LLVM scalarizes to per-lane libm logf.

Family progress

Intrinsic Status
exp_poly_f32 v1.11.0
tanh_approx_f32 v1.14.0 (PR #28)
log_approx_f32 v1.14.0 (this PR)
sin_cos_approx_f32 Future — speculative until a consumer asks

ROADMAP "Future API consistency" line trimmed accordingly — only sin_cos_approx_f32 remains, with open design questions on angle range, periodicity strategy, and API shape.

Cody-Waite ln(2) discipline

The recombine order matters: small-magnitude terms first (-u²/2, then e · LN2_LO), dominant terms last (+u, then e · LN2_HI). Same discipline exp_poly_f32 uses for ldexp; the then_exp_poly_roundtrip test pins that both intrinsics keep this contract.

Test plan

  • cargo test --features llvm --release — 937 tests pass (924 baseline + 13 new)
  • cargo clippy --features llvm --all-targets — clean
  • cargo fmt --check — clean
  • log_approx_f32(splat(1.0)) returns 0 (smoke)
  • Boundary points vs libm logf: powers of 2 from 1/1024 to 1e9, e, π, the √2/2 rebalance boundary; abs error ≤ 3e-6
  • 256 log-uniform random samples over [0.01, 100] for f32x4 and f32x8
  • Roundtrip: exp_poly_f32(log_approx_f32(x)) ≈ x to 1e-4 relative (composition guard)
  • IR guards: no @llvm.log; ≥8 FMAs; lshr <N x i32>; float↔int bitcasts; sitofp
  • Typeck rejections: scalar f32, f64x2, i32x4, wrong arity

🤖 Generated with Claude Code

@petlukk
petlukk force-pushed the feat/v1.14-log-approx-f32 branch 2 times, most recently from 2c2c24b to cb7119a Compare May 19, 2026 07:07
petlukk and others added 3 commits May 19, 2026 08:44
Eigen/Cephes-family approach — bit-level decomposition of x = m·2^e
(frexp convention, m in [0.5, 1)), √2/2 rebalance to center the
polynomial range, degree-8 Eigen-coefficient Horner in (m - 1),
Cody-Waite recombine with e·ln(2). Avoids @llvm.log which LLVM
scalarizes to per-lane libm logf.

Verbatim Eigen MathFunctionsImpl.h coefficients (MPL2) kept at
upstream's published double precision for diff-friendliness; the
compiler truncates each to f32 at compile time. LN2_HI is exact in
f32 (binary 0.10110001 1, 9 mantissa bits); LN2_LO carries the
residual so e·ln(2) preserves precision for large e.

Cody-Waite recombine order matters: small-magnitude terms first
(-u²/2, then e·LN2_LO), dominant terms last (+u, then e·LN2_HI).
Same discipline exp_poly_f32 uses for ldexp.

Max abs error ~3e-6 across (0, +∞). Same f32-vector-only typeck
shape as exp_poly_f32 and tanh_approx_f32.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 tests mirroring phase14_exp_poly.rs / phase14_tanh_approx.rs:

- at_one: smoke that log(1) = 0 in all 4 lanes.
- boundary_points (x4 + x8): powers of 2 across the f32 exponent
  range (1/1024 to 1e9), transcendental constants (e, π), and the
  rebalance boundary [√2/2, √2]. Absolute (not relative) error
  ≤ 3e-6 because log(x) → 0 as x → 1 makes relative error explode
  near 1.
- random (x4 + x8): 256 log-uniform samples over [exp(-4.6),
  exp(4.6)] ≈ [0.01, 100] — covers the dense middle of the input
  distribution.
- then_exp_poly_roundtrip: exp_poly_f32(log_approx_f32(x)) ≈ x to
  1e-4 relative. Pins composition of the two intrinsics — they
  share the Cody-Waite ln(2) discipline, so an order-of-operations
  regression in either would surface here.
- does_not_emit_llvm_log: IR guard against future simplification
  that delegates to @llvm.log (which LLVM scalarizes).
- emits_expected_pattern (x4 + x8): IR has ≥8 FMAs + lshr + float
  ↔ int bitcasts + sitofp.
- Four typeck rejections: scalar f32, f64x2, i32x4, wrong arity.

Boundary point constants use std::f32::consts::{FRAC_1_SQRT_2,
SQRT_2} per clippy's approx_constant lint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CHANGELOG entry under v1.14.0 Added. ROADMAP gets a Shipped entry;
the Future API consistency line is rewritten from a three-item bullet
(tanh/log/sin_cos) to just sin_cos_approx_f32 — the remaining
transcendental in the original trio, kept as speculative until a
real consumer asks (angle range, periodicity strategy, and
sin-vs-cos-vs-pair API shape are all open design questions).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@petlukk
petlukk force-pushed the feat/v1.14-log-approx-f32 branch from cb7119a to c1dd6a1 Compare May 19, 2026 08:45
@petlukk
petlukk merged commit a30c147 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