pow, log and exp
.NET Test / .NET tests (push) Successful in 1m31s

This commit is contained in:
2026-09-15 00:33:31 +04:00
parent beb9a084d0
commit b54a0a2d42
18 changed files with 2890 additions and 12 deletions
+70 -4
View File
@@ -75,11 +75,11 @@ DoubleDouble quarterTurn = DoubleDouble.PiOver2;
The factors avoid deriving constants at runtime; the multiplication itself remains
approximate DD arithmetic, so conversions are not guaranteed exact round trips.
The list is mathematical and dimensionless, not a table of unit-dependent physical
constants. Precomputed logarithms do not imply a general `Log` function is implemented.
constants. The natural logarithm function is provided separately by `DDMath.Log`.
## Mathematical functions
The initial `DDMath` static class provides:
The `DDMath` static class provides:
- `Abs(DoubleDouble)`: preserves both components, maps either signed zero to
positive zero and either infinity to positive infinity, and returns canonical NaN.
@@ -88,12 +88,40 @@ The initial `DDMath` static class provides:
to retain extended precision, including for subnormal inputs, without squaring
an unscaled estimate near the exponent limits. Signed zero and positive infinity
are preserved; negative nonzero inputs and NaN return canonical NaN.
- `Pow(DoubleDouble, int)`: exponentiation by squaring with a separately tracked
binary exponent. Supports the full `int` domain, including `int.MinValue`, and
reciprocates a bounded significand before final scaling for negative exponents.
Any value to power zero is one (including NaN and signed zero). Other NaNs
propagate; zero/infinity signs follow integer-power parity and exponent sign.
- `Pow(DoubleDouble, double)` and `Pow(DoubleDouble, DoubleDouble)`: arbitrary
real powers, retaining the base's low component and, for the DD overload, the
exponent's low component. For finite exponents, negative finite bases require integers;
integrality and odd/even parity use the complete exponent, even above `2^53`.
Integer-valued exponents within `int` range reuse the integer implementation.
Other finite cases share `Log`'s range-scaled finite kernel, followed by `Exp`.
Near-one bases retain tiny low components before multiplication by large powers.
- `Exp(DoubleDouble)`: binary range reduction with three components of `ln(2)`,
followed by a [12/12] Padé approximation and power-of-two scaling. Both input
components affect range boundaries; representable subnormals are retained.
Either zero maps to one, negative infinity to positive zero, positive infinity
to positive infinity, and NaN to canonical NaN.
- `Log(DoubleDouble)`: natural logarithm using binary range reduction and a
centered atanh series. The bounded mantissa avoids denominator overflow for
large inputs; a separate near-one path retains even minimum-subnormal low
components. Either zero maps to negative infinity, one to positive zero,
positive infinity to itself, and negative nonzero inputs or NaN to canonical NaN.
```csharp
using Just.PreciseMath;
DoubleDouble root = DDMath.Sqrt(new DoubleDouble(2.0));
DoubleDouble magnitude = DDMath.Abs(-root);
DoubleDouble smallPower = DDMath.Pow(new DoubleDouble(2.0), -1024);
DoubleDouble exponential = DDMath.Exp(new DoubleDouble(1.0));
DoubleDouble logarithm = DDMath.Log(new DoubleDouble(10.0));
DoubleDouble fractionalPower = DDMath.Pow(new DoubleDouble(2.0), 0.5);
DoubleDouble preciseExponent = DoubleDouble.FromComponents(0.5, 1e-30);
DoubleDouble precisePower = DDMath.Pow(new DoubleDouble(2.0), preciseExponent);
```
Square-root tests compare the exact component sum against a `2^-100` relative
@@ -102,6 +130,44 @@ exponent, boundary neighbors, both signs of the low component, and exact binary
squares. This is a tested approximate-accuracy contract, not exhaustive coverage
of all component pairs or a guarantee of correctly rounded results.
Logarithm tests compare exact component sums with independently generated
120-digit decimal references, requiring agreement at 450 and 650 digits. They
check `2^-100` relative error plus one minimum binary64 subnormal, with an explicit
reference-rounding allowance. Powers of two cover every finite binary64 exponent;
additional tests cover extreme magnitudes, near-one cancellation, sparse lows of
either sign, and range-reduction transitions. This is sampled approximate accuracy,
not a universal error proof or a correct-rounding guarantee.
Exponential tests use independent high-precision decimal references evaluated at
two precisions, with exact integer comparisons of the stored component sum. They
check `2^-100` relative error plus one minimum binary64 subnormal, with a separately
bounded reference-rounding allowance. Integer-power tests use exact rational
references and high-precision fixtures for large exponents; their tested absolute
error bound is `|exact result| * (|exponent| + 1) * 2^-100 + 2^-1074`, not uniform
relative accuracy independent of the exponent. Both functions are approximate;
precision decreases near underflow and approximation can affect results extremely
close to a rounding boundary. Final scaling uses the existing allocating exact
boundary machinery where necessary. No performance measurements are claimed.
For real powers, `x^±0 = 1` and `1^y = 1`, including NaN in the other operand;
other NaNs propagate. Infinite exponents compare the complete `|x|` with one,
with `(-1)^±∞ = 1`. Zero and infinite bases yield a negative sign only for odd
integer exponents; negative powers exchange zero and infinity. Finite negative
bases with non-integer finite exponents return canonical NaN.
Real-power reference tests check `2^-90` relative error plus one minimum binary64
subnormal for the logarithm/Exp path, with an explicit reference-rounding allowance.
Integer dispatch retains the exponent-dependent bound above. These are tested
approximate-accuracy contracts, not universal error proofs or correct-rounding
guarantees. Reference inputs are exact component sums; logarithms/exponentials
are independently evaluated at 450 and 650 decimal digits.
**Known real-power limitations:** extremely close to the range boundaries, the
logarithm/Exp path can return infinity for a mathematically finite result, or a
minimum subnormal where zero is expected. The latter can also break monotonicity
across integer-exponent dispatch. These issues remain unresolved; passing the
sampled error bounds does not guarantee correct range decisions for every input.
## Conversions and formatting
- Explicit conversions support `double`, `float`, `int`, `long`, and `decimal`
@@ -189,8 +255,8 @@ bool success = DoubleDouble.TryParse("1.25e-2".AsSpan(), CultureInfo.InvariantCu
## Deferred scope
The `DDMath.Pow`, `DDMath.Exp`, and `DDMath.Log` functions remain unimplemented.
Generic-math interfaces beyond `ISignedNumber`,
Natural `DDMath.Log`, `Exp`, and all three `Pow` overloads are implemented.
Logarithms in other bases, generic-math interfaces beyond `ISignedNumber`,
additional text formats/general round-trip formatting, and non-arithmetic performance
benchmarks remain deferred.
Replacing allocating arithmetic boundary fallbacks is also deferred; the current