square function and some sanity checks
.NET Test / .NET tests (push) Successful in 1m35s

This commit is contained in:
2026-09-18 16:59:28 +04:00
parent 5797bf4884
commit 620faca7eb
10 changed files with 455 additions and 28 deletions
+29 -2
View File
@@ -109,6 +109,15 @@ 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.
It shares the existing `DoubleDouble.Abs` implementation.
- `Square(DoubleDouble)`: forwards to the specialized `DoubleDouble.Square` kernel.
It combines the equal cross terms with FMA, normalizes, then incorporates the
low-component square without losing selected exactly representable tails.
Either signed zero maps to positive zero, either infinity to positive infinity,
and NaN to canonical NaN. It shares multiplication's allocating boundary fallback
and tested `2^-100` relative error plus one minimum binary64 subnormal bound.
Results can differ in low bits from `value * value`; neither route is universally
correctly rounded. No speedup has been measured, and existing kernels have not
been rewritten to use it.
- `Reciprocal(DoubleDouble)`: returns exactly the same high and low component bits
as `1.0 / value`. It specializes scalar/DD division for a numerator of one,
omitting redundant numerator checks and sharing the finite scalar-numerator kernel,
@@ -118,7 +127,11 @@ The `DDMath` static class provides:
No speedup over scalar/DD division has been measured.
- `Sqrt(DoubleDouble)`: uses power-of-two scaling and an FMA-based Newton correction
to retain extended precision, including for subnormal inputs, without squaring
an unscaled estimate near the exponent limits. Signed zero and positive infinity
an unscaled estimate near the exponent limits. Sparse lows endangered by input
scaling or correction division are applied separately with tracked exponents:
for example, `sqrt((2^1000, 2^-174))` retains the low component `2^-675`.
This exceptional correction can reach the allocating boundary machinery;
ordinary inputs retain the original scaled correction path. Signed zero and positive infinity
are preserved; negative nonzero inputs and NaN return canonical NaN.
- `InvSqrt(DoubleDouble)`: computes the reciprocal square root with power-of-two
scaling and a compensated Newton step, avoiding double-double division and its
@@ -195,6 +208,7 @@ The `DDMath` static class provides:
using Just.PreciseMath;
DoubleDouble root = DoubleDouble.Sqrt(new DoubleDouble(2.0));
DoubleDouble square = DoubleDouble.Square(root);
DoubleDouble inverseRoot = DoubleDouble.InvSqrt(new DoubleDouble(2.0));
DoubleDouble cubeRoot = DoubleDouble.Cbrt(new DoubleDouble(-8.0));
DoubleDouble distance = DoubleDouble.Hypot(new DoubleDouble(3.0), new DoubleDouble(4.0));
@@ -227,6 +241,9 @@ squares/powers of four. Square-root tests also bracket exact root-rounding midpo
both suites exercise half-ulp low-component normalization boundaries.
This is a tested approximate-accuracy contract, not exhaustive coverage
of all component pairs or a guarantee of correctly rounded results.
Square-root tests separately verify selected sparse output residuals with exact
squared midpoint inequalities, including either low sign, dispatch neighbors,
and corrections that become representable only after rescaling.
Cube-root and nth-root tests check `2^-100` relative error for finite nonzero
inputs with degree magnitude at least two. Small degrees use exact integer-power
@@ -306,6 +323,14 @@ sampled error bounds does not guarantee correct range decisions for every input.
- `TryFormat(Span<char>, ...)` implements `ISpanFormattable` with the same formats.
It currently allocates via `ToString`; insufficient space returns `false`, writes
zero characters, and leaves the destination unchanged.
- `ToStringExact()` emits the complete decimal expansion of the stored component
sum, without rounding, scientific notation, or unnecessary fractional zeros.
Unlike ordinary `ToString`, it always uses **invariant culture**. It preserves
negative zero and emits `NaN`, `Infinity`, or `-Infinity` for special values.
It allocates through exact integer arithmetic; sparse pairs can require over
a thousand characters. Use `DoubleDouble.Parse(text, CultureInfo.InvariantCulture)`
to recover the components. This is not a shortest-round-trip format and does not
change the standard formats' 999-digit precision limit or the parser's length limit.
`DoubleDouble` implements `ISignedNumber<DoubleDouble>`, including the inherited
`INumberBase` contracts: binary radix, classification, absolute value, magnitude
@@ -374,7 +399,7 @@ bool success = DoubleDouble.TryParse("1.25e-2".AsSpan(), CultureInfo.InvariantCu
Natural `DDMath.Log`, the complete exponential family on `DoubleDouble`, and all
three `DDMath.Pow` overloads are implemented.
Logarithms in other bases, generic-math interfaces beyond `ISignedNumber`,
`IFloatingPointConstants`, `IRootFunctions`, and `IExponentialFunctions`, additional text formats/general
`IFloatingPointConstants`, `IRootFunctions`, and `IExponentialFunctions`, additional text formats/shortest-
round-trip formatting, and non-arithmetic performance benchmarks remain deferred.
Replacing allocating arithmetic boundary fallbacks is also deferred; the current
`BigInteger` paths remain in place. That optimization does not require removing
@@ -400,6 +425,8 @@ on CI workflow runs.
BenchmarkDotNet measures arithmetic throughput, dependent-chain latency, and allocations,
including comparisons of `DoubleDouble`, `decimal`, and `double`, mixed scalar operations,
and exponent-boundary paths. These are performance measurements, not accuracy tests.
Squaring cases compare `DoubleDouble.Square(value)` with `value * value`; their
presence is not evidence of a speedup.
After the Release build above, run from the repository root: