Skip to content

PreciseNumber implements INumber<T> and nothing else, so there is no sqrt, exp, log or trig #78

Description

@matt-edmondson

Tracking issue. Found while speccing an orbital-mechanics showcase (SGP4 propagation) that wants PreciseNumber as its reference arithmetic.

PreciseNumber declares : INumber<PreciseNumber> and stops there. It has none of:

  • IRootFunctions<PreciseNumber> — no Sqrt, Cbrt, RootN, Hypot
  • IExponentialFunctions<PreciseNumber> / ILogarithmicFunctions<PreciseNumber>
  • ITrigonometricFunctions<PreciseNumber> — no Sin, Cos, Tan, Asin, …
  • IPowerFunctions<PreciseNumber>Pow exists as an instance method and falls back to double for non-integer exponents
  • IHyperbolicFunctions<PreciseNumber>

Two consequences. A caller cannot write an algorithm generically over INumber<T> plus any of these constraints and have PreciseNumber satisfy it. And more sharply: taking a square root today means Math.Sqrt(x.To<double>()), so a 50-digit number that takes one root is a 15-digit number wearing a 50-digit type, silently.

What already exists and should be reused rather than re-derived

  • ReduceSignificance(int significantDigits) is public and rounds half away from zero. Every series below needs it: multiplication is exact, so x^n for a 50-digit x carries 50n digits, and an un-truncated Taylor series grows quadratically in memory for no accuracy gain.
  • Divide(left, right, int significantDigits) establishes the precedent for a precision-taking overload sitting next to a default one. The new functions should follow it exactly rather than invent a second convention.
  • ktsu.Semantics.Quantities' internal StorageMath.Sqrt<T> already implements a type-generic Newton square root — double-seeded, scaled by powers of four for values outside double's range, throwing rather than returning an unconverged estimate. It is the algorithm the roots sub-issue wants, already tested. (Opening it up is ktsu-dev/Semantics — separate issue there.)

Design decisions that apply to all of the sub-issues

Precision contract. None of these interface members takes a precision argument, so each computes at MinimumDivisionPrecision (50), matching Divide. Alongside each, add an explicit (value, int significantDigits) overload, mirroring Divide.

Accuracy contract: faithful rounding, not correct rounding. The result is within 1 ulp at the requested precision. Correct rounding runs into the table-maker's dilemma and is not worth the cost here. Document that, and do not claim more than it.

Guard digits. Compute internally at significantDigits + 10, truncate every intermediate to that working precision with ReduceSignificance, and reduce once at the end.

Termination. Sum until a term's exponent falls more than the working precision below the accumulator's, then take one more term. Never a fixed iteration count; non-convergence is an ArithmeticException, following StorageMath's precedent.

Atan2 is not on ITrigonometricFunctions. It lives on IFloatingPointIeee754<T>, which PreciseNumber should not implement — it is not IEEE-754 and has no NaN or infinity. So Atan2 is a bespoke static, added in the trig sub-issue because SGP4 cannot be written without it.

Sub-issues, in dependency order

  1. IRootFunctions<PreciseNumber> — no dependencies
  2. IExponentialFunctions + ILogarithmicFunctions + IPowerFunctions — no dependencies; also fixes Pow's silent double fallback
  3. ITrigonometricFunctions + Atan2 — depends on both of the above and on the constants issue

IHyperbolicFunctions is cheap once exp/log land and can follow later.

Acceptance for the set

  • Each function checked against published 50-digit reference values (√2, ln 2, sin 1, cos 1, e, …)
  • Identity tests at 50 digits: sin²x + cos²x = 1, Exp(Log(x)) = x, Sqrt(x)² = x, Atan2 in all four quadrants
  • Agreement with the double answer to 15 digits across a sweep — the cheap regression net
  • A benchmark class per function in PreciseNumber.Benchmarks, parameterised by the repo's Digits axis (8 / 30 / 200) with allocation reported, so the cost of each is on the record before it ships. Related: Investigate benchmark regressions in Max, CompareTo, ReduceSignificance, Pow, and Divide since 2.0 #76.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions