* Direct-log fixed-point natural logarithm (machine-seed + log1p correction). * * ln(v) = y₀ + log1p(v·e^{−y₀} − 1), where y₀ = Math.log(v) is a ~48-bit-accurate * float64 seed. Because v·e^{−y₀} = 1 + ε with ε ≈ 2^{−48}, the correction * log1p(ε) = 2·atanh(ε/(2+ε)) needs only ≈ bits/96 series t
(x: bigint, bits: number)
| 389 | * @returns ln(x/2^bits) * 2^bits as a bigint. |
| 390 | */ |
| 391 | function fplnDirect(x: bigint, bits: number): bigint { |
| 392 | const B = BigInt(bits); |
| 393 | const scale = 1n << B; |
| 394 | |
| 395 | // Overflow-safe ~48-bit float seed y₀ ≈ ln(x/scale)·scale. Extract the top |
| 396 | // ~52 bits of x so Number() stays finite even when 2^bits overflows a double |
| 397 | // (bits ≥ 1024); the log1p correction restores full precision regardless. |
| 398 | const topBits = 52; |
| 399 | const sh = bits - topBits; |
| 400 | let y0Fp: bigint; |
| 401 | if (sh > 0) { |
| 402 | const v = Number(x >> BigInt(sh)) / 2 ** topBits; |
| 403 | y0Fp = BigInt(Math.round(Math.log(v) * 2 ** topBits)) << BigInt(sh); |
| 404 | } else { |
| 405 | const v = Number(x) / Number(scale); |
| 406 | y0Fp = BigInt(Math.round(Math.log(v) * Number(scale))); |
| 407 | } |
| 408 | |
| 409 | // p = x·e^{−y₀} = (1 + ε)·scale, ε ≈ 2^{−48}. The single fpexp call. |
| 410 | const p = (x * fpexp(-y0Fp, bits)) >> B; |
| 411 | const eps = p - scale; // ε·scale (may be negative if y₀ overshot ln v) |
| 412 | |
| 413 | // log1p(ε) = 2·atanh(u), u = ε/(2+ε). The atanh terms all share u's sign, so |
| 414 | // sum in magnitude: an arithmetic right-shift of a negative bigint floors |
| 415 | // toward −∞ and sticks at −1 instead of reaching 0, which would never |
| 416 | // terminate the loop. |
| 417 | const u = (eps << B) / (p + scale); |
| 418 | const uAbs = u < 0n ? -u : u; |
| 419 | const u2 = (uAbs * uAbs) >> B; |
| 420 | let term = uAbs; |
| 421 | let sum = uAbs; |
| 422 | for (let n = 3n; ; n += 2n) { |
| 423 | term = (term * u2) >> B; |
| 424 | if (term === 0n) break; |
| 425 | sum += term / n; |
| 426 | } |
| 427 | |
| 428 | return y0Fp + (u < 0n ? -2n : 2n) * sum; |
| 429 | } |
| 430 | |
| 431 | /** |
| 432 | * ln(2) · 2^bits, cached. Computed once via the Newton path (NOT the AGM path, |