Functional Weave
Code in Rust

math.ln@1.0.0

README.md

1,612 bytes · view raw

# math.ln

The natural logarithm, computed with nothing but `+`, `-`, `*` and `/`, so
TypeScript, Python and Rust return the same double for the same input.

`Math.log`, Python's `math.log` and Rust's `f64::ln` all call their
platform's maths library, and IEEE 754 does not require those to be correctly
rounded: the last bit can differ between a browser, CPython on glibc and Rust
on macOS. Usually that does not matter. It does when the result is then
rounded for display or compared against a threshold, because a value next to
the boundary goes one way in one language and the other way in another. A log
scale, a colour conversion or a growth rate that must agree across languages
uses this instead.

## Method

1. Write x = m x 2^e with m in (sqrt(1/2), sqrt(2)], by halving or doubling,
   which is exact for every double, subnormals included.
2. With s = (m - 1) / (m + 1), |s| < 0.172, ln m = 2 atanh s =
   2s (1 + s²/3 + s⁴/5 + ... + s²⁴/25), summed by Horner's rule.
3. ln x = e ln 2 + ln m, with ln 2 split into a high part whose trailing bits
   are zero (so e x high is exact) and a low correction, as in fdlibm.

The result is within 2 units in the last place of the true logarithm over the
whole positive range (checked against a correctly rounded reference), and
bit-identical in all three languages because every step is an IEEE operation
performed in the same order. `ln(1)` is exactly 0.

Zero, negative numbers, infinity and NaN are errors.

Sources: W. J. Cody and W. Waite, *Software Manual for the Elementary
Functions* (1980); Sun Microsystems fdlibm, `e_log.c` (the ln 2 split).