Functional Weave
Code in Python

math.fractional-power@1.0.0

README.md

1,944 bytes · view raw

# math.fractional-power

Raises a positive decimal to a rational power, x^(p/q), and gets the same
digits in TypeScript, Python and Rust. Finance needs this wherever a rate is
compounded over part of a year: the consumer-credit APR and early-settlement
formulas raise (1 + APR) to the power "days / 365", and a monthly factor is
(1 + APR)^(1/12). Those are irrational numbers, so no exact answer exists;
floating-point `Math.pow`, `**` and `powf` do not promise the same last digit
on every platform, and a settlement figure must not depend on the platform.

## How it is computed

Everything is an integer count of 10^-18 (18-place fixed point), and every
step floors, in a fixed order, so the three languages agree bit for bit:

1. The q-th root: the largest y with y^q <= x, found by bisection.
2. The power: y^|p| by square-and-multiply from the lowest bit, flooring each
   product.
3. For a negative p, the reciprocal, floored.

The vectored function returns the result rounded half-up to 12 decimal
places. Each floor loses under 10^-18, so the 12th place is right unless the
true value lies within roughly 10^-15 of a rounding boundary. Exact powers
that fit in 18 places come out exact: 1.1^2 is 1.21.

Other capabilities use the fixed-point pieces directly: `FIXED_SCALE`,
`mulFixed`, `powFixed`, `powFixedBounded`, `rootFixed`,
`fractionalPowerFixed` and `parseFixed` (snake_case in Python and Rust; the
Rust versions take `math.big-integer`'s `BigInt`).

## Limits

- The base is a positive decimal with at most 18 places; zero and negatives
  are refused (a fractional power of a negative number is not real).
- The exponent's numerator is -100000 to 100000 and its denominator 1 to
  100000. The fraction need not be in lowest terms: 62/730 gives the same
  answer as 31/365 up to the floors, and in practice the same 12 places.
- A value that would pass 2^512 in fixed point (about 10^136) is an error,
  not a slow computation.