This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user