Files
rippled/include
Vito 26f82a2a16 fix: Numerically-stable (1+r)^n-1 in computePaymentFactor
At near-zero `periodicRate`, the direct subtraction
`power(1 + r, n) - 1` suffers catastrophic cancellation: `(1+r)^n`
rounds to a value very close to 1 and the subtraction discards most
of Number's 19-digit large-mantissa precision. The resulting
amortization factor is inaccurate enough that
`loanPrincipalFromPeriodicPayment` returns a principal greater than
`periodicPayment * paymentsRemaining`, which propagates into
`computeTheoreticalLoanState` as a negative `interestDue` and fires
the `interest due delta not greater than outstanding` assertion in
`computePaymentComponents` (testBugInterestDueDeltaCrash).

The same numerical defect causes systematic underpayment of
interest in release builds at the bug regime. On a $1B loan with
the test parameters, closed-form charges only $0.0588 of interest
versus the mathematically correct $0.3805 (verified independently
via 50-digit Decimal arithmetic). Linear scaling: ~$321 underpaid
per $1T of principal.

Replace `raisedRate - 1` with a hybrid evaluator:

- When `r * paymentsRemaining >= 1e-9`, use the closed-form
  `power(1+r, n) - 1`. At Number's 19-digit mantissa this still
  retains ~10 sig digits post-subtraction and is ~30-500x faster
  than the binomial expansion.
- Below the threshold, expand `(1+r)^n - 1 = sum C(n,k) r^k` with
  early termination once terms fall below Number precision.

The hybrid takes the closed-form path at every rate covered by
existing fixtures, so output is bit-identical to pre-fix code at
moderate rates (no fixture drift).

Also drops the now-unused `computeRaisedRate` from the lending
helpers (its only caller was `computePaymentFactor`).

Test coverage:

- testComputePowerMinusOne / testComputePowerMinusOneHybrid:
  direct unit tests for both new helpers, including property
  checks against `(1+r)^2 = 1 + 2r + r^2` and `(1+r)^3 = 1 + 3r +
  3r^2 + r^3`, threshold-boundary verification at exactly
  r*n = 1e-9, and large-n early-termination.
- testLoanPrincipalFromPeriodicPaymentNearZeroRate: regression
  guard, asserts `principal <= payment * n` at near-zero rate.
- testComputeTheoreticalLoanStateNearZeroRate: regression guard,
  asserts `interestDue >= 0` and `principalOutstanding <=
  valueOutstanding`.
- testBugInterestDueDeltaCrash: end-to-end reproduction of the
  original assertion abort, now passes cleanly.
- testFullLifecycleVaultPnLNearZeroRate: integration test running
  a $1B loan to completion, verifies the vault collects the
  economically-correct interest matching the 50-digit Decimal
  reference within sub-microcent tolerance, plus self-consistency
  (vault gain == TVO - principal at LoanSet) and conservation
  (borrower outflow == vault gain + broker gain).
2026-04-27 18:43:00 +02:00
..