inv sqrt implementation
.NET Test / .NET tests (push) Successful in 1m32s

This commit is contained in:
2026-09-15 00:52:35 +04:00
parent b54a0a2d42
commit 1e14a72d1c
4 changed files with 331 additions and 3 deletions
+53
View File
@@ -55,6 +55,59 @@ public static partial class DDMath
return DoubleDouble.FromComponents(Math.ScaleB(root.High, rootExponent), Math.ScaleB(root.Low, rootExponent));
}
/// <summary>Returns the reciprocal square root with double-double precision.</summary>
/// <remarks>
/// Uses power-of-two scaling and a compensated Newton step without double-double
/// division. Results are approximate, not guaranteed correctly rounded; positive
/// finite inputs are tested against a relative error bound of 2^-100 across the
/// binary64 range. Positive/negative zero maps to positive/negative infinity,
/// positive infinity to positive zero, and negative nonzero values or NaN to
/// canonical NaN.
/// </remarks>
[Pure]
public static DoubleDouble InvSqrt(DoubleDouble value)
{
if (double.IsNaN(value.High) || value.High < 0.0)
{
return DoubleDouble.NaN;
}
if (value.High == 0.0)
{
return new DoubleDouble(Math.CopySign(double.PositiveInfinity, value.High));
}
if (double.IsPositiveInfinity(value.High))
{
return DoubleDouble.Zero;
}
// As in Sqrt, the even exponent is in [-1074, 1022], high is in [1, 4),
// and any underflow in a sparse scaled low is below the 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 = 1.0 / Math.Sqrt(high);
// With u=2^-53, the seed has O(u) relative error. Preserve the square's
// FMA residual before cancellation in 1 - (high + low)*estimate^2:
// using only the rounded square would leave O(u) error after refinement.
// All leading products are bounded and normal. Omitting low*squareError
// contributes only O(u^2), as does one Newton step's remaining error.
double square = estimate * estimate;
double squareError = Math.FusedMultiplyAdd(estimate, estimate, -square);
double residual = Math.FusedMultiplyAdd(-high, square, 1.0);
residual = Math.FusedMultiplyAdd(-high, squareError, residual);
residual = Math.FusedMultiplyAdd(-low, square, residual);
double correction = (0.5 * estimate) * residual;
DoubleDouble inverseRoot = DoubleDouble.FromComponents(estimate, correction);
// The result high is normal and finite over the entire positive input
// range. Rescale in the opposite direction to Sqrt and canonicalize any
// zero low, including underflow of an exceptionally sparse correction.
int inverseExponent = -(exponent / 2);
return DoubleDouble.FromComponents(Math.ScaleB(inverseRoot.High, inverseExponent),
Math.ScaleB(inverseRoot.Low, inverseExponent));
}
// For finite, nonzero normalized significands and exponents bounded by the
// integer-power domain (|exponent| < 2^42). Keep the exponent separate until
// the final result so an intermediate cannot overflow before reciprocation.