(a: bigint, bits: number)
| 85 | * Converge until |x_{n+1} - x_n| <= 1 (one ULP in the fixed-point grid). |
| 86 | */ |
| 87 | export function fpsqrt(a: bigint, bits: number): bigint { |
| 88 | if (a === 0n) return 0n; |
| 89 | if (a < 0n) throw new RangeError('fpsqrt: negative input'); |
| 90 | |
| 91 | // as = a * scale = a << bits; the result is isqrt(as). |
| 92 | const as = a << BigInt(bits); |
| 93 | |
| 94 | // Seed the final refinement. At low/medium precision the float-seeded Heron |
| 95 | // converges in a few full-width divisions and wins outright; at high |
| 96 | // precision a recursive giant-steps isqrt (root the top half, then refine) |
| 97 | // does ~3× fewer full-width divisions. Dispatch on `bits` (a plain number |
| 98 | // compare, no bitLength) so the hot low-precision path is unchanged — fpsqrt |
| 99 | // callers pass a ≈ O(1)·2^bits, so as ≈ 2^(2·bits) and bits ≥ FP_SQRT_GIANT_BITS |
| 100 | // ⇒ as ≳ SQRT_GIANT_MIN_BITS (and isqrtGiant falls back to flat Heron if not). |
| 101 | let x: bigint; |
| 102 | if (bits < FP_SQRT_GIANT_BITS) { |
| 103 | x = bigSqrtSeed(as); |
| 104 | let prev: bigint; |
| 105 | do { |
| 106 | prev = x; |
| 107 | x = (x + as / x) / 2n; |
| 108 | } while (bigintAbs(x - prev) > 1n); |
| 109 | } else { |
| 110 | x = isqrtGiant(as, bitLength(as)); |
| 111 | } |
| 112 | |
| 113 | // One more iteration, then pick whichever of {x, next} has x² |
| 114 | // closest to `as` (the true floor-root or one above it). |
| 115 | const next = (x + as / x) / 2n; |
| 116 | const diffX = bigintAbs(x * x - as); |
| 117 | const diffNext = bigintAbs(next * next - as); |
| 118 | return diffNext < diffX ? next : x; |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * Seed for an integer square root of `n`: a value within a few bits of |
no test coverage detected