initial DDMath implementation
.NET Test / .NET tests (push) Successful in 1m35s

This commit is contained in:
2026-09-14 22:25:50 +04:00
parent 4efecbb1bc
commit beb9a084d0
4 changed files with 281 additions and 4 deletions
+57
View File
@@ -0,0 +1,57 @@
namespace Just.PreciseMath;
/// <summary>Provides mathematical functions for normalized double-double values.</summary>
/// <remarks>Results use fixed-size double-double precision, not arbitrary precision.</remarks>
public static class DDMath
{
/// <summary>Returns the absolute value without discarding the low component.</summary>
/// <remarks>Both signs of zero become positive zero; NaN is canonicalized and either infinity becomes positive infinity.</remarks>
[Pure]
public static DoubleDouble Abs(DoubleDouble value)
{
return DoubleDouble.Abs(value);
}
/// <summary>Returns the nonnegative square root with double-double precision.</summary>
/// <remarks>
/// Uses power-of-two scaling and an FMA-based Newton correction. Results are
/// approximate, not guaranteed correctly rounded; finite positive inputs are
/// tested against a relative error bound of 2^-100 across the binary64 range.
/// Signed zero and positive infinity are preserved. Negative nonzero values
/// (including negative infinity) and NaN return canonical NaN.
/// </remarks>
[Pure]
public static DoubleDouble Sqrt(DoubleDouble value)
{
if (double.IsNaN(value.High) || value.High < 0.0)
{
return DoubleDouble.NaN;
}
if (value.High == 0.0 || double.IsPositiveInfinity(value.High))
{
return value;
}
// Choose an even exponent, rounding negative odd exponents down too.
// For positive finite normalized inputs, exponent is in [-1074, 1022]
// and the scaled high is in [1, 4). Scaling the high is exact; any
// underflow in a very sparse low is far below the relative error bound.
int exponent = Math.ILogB(value.High) & ~1;
double high = Math.ScaleB(value.High, -exponent);
double low = Math.ScaleB(value.Low, -exponent);
double estimate = Math.Sqrt(high);
// Estimate is in [1, 2]. FMA avoids rounding its square before
// cancellation. One Newton correction reduces O(u) error to O(u^2),
// where u=2^-53; both components of the input contribute to the residual.
double residual = Math.FusedMultiplyAdd(-estimate, estimate, high) + low;
double correction = residual / (2.0 * estimate);
DoubleDouble root = DoubleDouble.FromComponents(estimate, correction);
// The root high stays normal, even if rounding reaches the next binade.
// Normalize again to canonicalize a zero low, including any underflow
// of an exceptionally sparse correction during rescaling.
int rootExponent = exponent / 2;
return DoubleDouble.FromComponents(Math.ScaleB(root.High, rootExponent), Math.ScaleB(root.Low, rootExponent));
}
}