UNPKG

exactnumber

Version:

Arbitrary-precision decimals. Enables making math calculations with rational numbers, without precision loss.

81 lines (63 loc) 2.38 kB
import { FixedNumber } from '../FixedNumber'; import type { ExactNumberType } from '../types'; import { Fraction } from '../Fraction'; import { ExactNumber } from '../ExactNumber'; import { _0N, _1N } from '../util'; const approximateNthRoot = (n: number, x: ExactNumberType): string => { let xNum = x.toNumber(); if (Number.isFinite(xNum)) { // JS doesn't work well with powers of negative numbers const isNegative = xNum < 0; if (isNegative) { xNum = -xNum; } let guess = xNum ** (1 / n); if (isNegative) { guess = -guess; } return guess.toString(); } // approximate number of digits in the output number const xDigits = x.abs().toFixed(0).length; const outputDigits = Math.ceil(xDigits / n); const sign = x.sign() === 1 ? '' : '-'; return `${sign}5e${outputDigits}`; }; // Newton's method const nthrootWithNewton = (n: number, x: ExactNumberType, decimals: number): ExactNumberType => { const initialGuess = approximateNthRoot(n, x); let xk = new FixedNumber(initialGuess !== '0' ? initialGuess : '1') as ExactNumberType; const c0 = new Fraction(n - 1, n); const c1 = new Fraction(x, n); const c2 = BigInt(n - 1); let last = xk.trunc(decimals + 5); while (true) { xk = c0.mul(xk).add(c1.mul(xk.pow(c2).inv())); xk = xk.trunc(decimals + 5); if (xk.isZero() || last.eq(xk)) { break; } last = xk; } return xk.trunc(decimals); }; export const nthroot = ( n: number, x: number | bigint | string | ExactNumberType, decimals: number, ): ExactNumberType => { if (!Number.isSafeInteger(n)) throw new Error('Integer is expected for N'); if (n < 0) throw new Error('Negative N is not supported'); if (n === 0) throw new Error('N cannot be zero'); const xNum = ExactNumber(x); if (n === 1) { return xNum.trunc(decimals); } if (n % 2 === 0 && xNum.sign() === -1) throw new Error('Complex numbers are not supported'); if (xNum.isZero()) return new FixedNumber(_0N).trunc(decimals); if (xNum.isOne()) return new FixedNumber(_1N).trunc(decimals); const res = nthrootWithNewton(n, xNum, decimals); return res; }; export const sqrt = (x: number | bigint | string | ExactNumberType, decimals: number) => nthroot(2, x, decimals); export const cbrt = (x: number | bigint | string | ExactNumberType, decimals: number) => nthroot(3, x, decimals);