|
|
|
@@ -0,0 +1,436 @@
|
|
|
|
|
namespace Just.PreciseMath;
|
|
|
|
|
|
|
|
|
|
public readonly partial struct DoubleDouble : IRootFunctions<DoubleDouble>
|
|
|
|
|
{
|
|
|
|
|
/// <summary>Returns the real cube root with double-double precision.</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// Preserves signed zero and signed infinity; NaN returns canonical NaN.
|
|
|
|
|
/// Scales to a bounded binade before two compensated Newton corrections.
|
|
|
|
|
/// Results are approximate, not guaranteed correctly rounded; finite nonzero
|
|
|
|
|
/// inputs are tested against a relative error bound of 2^-100.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
[Pure]
|
|
|
|
|
public static DoubleDouble Cbrt(DoubleDouble value)
|
|
|
|
|
{
|
|
|
|
|
if (!IsFinite(value) || IsZero(value))
|
|
|
|
|
{
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DoubleDouble magnitude = Abs(value);
|
|
|
|
|
int exponent = Math.ILogB(magnitude.High);
|
|
|
|
|
int remainder = ((exponent % 3) + 3) % 3;
|
|
|
|
|
exponent -= remainder;
|
|
|
|
|
// Scaled high is in [1, 8), including subnormal original inputs.
|
|
|
|
|
DoubleDouble scaled = new(Math.ScaleB(magnitude.High, -exponent));
|
|
|
|
|
DoubleDouble root = new(Math.Cbrt(scaled.High));
|
|
|
|
|
DoubleDouble square = root * root;
|
|
|
|
|
// First improve the binary64 seed (Math.Cbrt need not be correctly rounded).
|
|
|
|
|
// Re-anchor at the refined high, then derive its residual afresh; this
|
|
|
|
|
// also avoids retaining Newton iteration noise for exact binary cubes.
|
|
|
|
|
root = new DoubleDouble((root + ((scaled - (square * root)) / (square * 3.0))).High);
|
|
|
|
|
square = root * root;
|
|
|
|
|
root += (scaled - (square * root)) / (square * 3.0);
|
|
|
|
|
int rootExponent = exponent / 3;
|
|
|
|
|
root = FromComponents(Math.ScaleB(root.High, rootExponent), Math.ScaleB(root.Low, rootExponent));
|
|
|
|
|
root += RootLowCorrection(magnitude, root, 3);
|
|
|
|
|
return value.High < 0.0 ? -root : root;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Returns sqrt(x² + y²) without overflowing intermediate squares.</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// The result is nonnegative, including positive zero. Either infinity takes
|
|
|
|
|
/// precedence over NaN; otherwise NaN propagates. Uses scaled squares and an
|
|
|
|
|
/// exact allocating rescale at exponent boundaries, with exact squared-input
|
|
|
|
|
/// overflow classification. Approximate finite results
|
|
|
|
|
/// are tested against 2^-100 relative error plus one minimum binary64 subnormal;
|
|
|
|
|
/// correctly rounded finite components are not guaranteed.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
[Pure]
|
|
|
|
|
public static DoubleDouble Hypot(DoubleDouble x, DoubleDouble y)
|
|
|
|
|
{
|
|
|
|
|
if (IsInfinity(x) || IsInfinity(y))
|
|
|
|
|
{
|
|
|
|
|
return PositiveInfinity;
|
|
|
|
|
}
|
|
|
|
|
if (IsNaN(x) || IsNaN(y))
|
|
|
|
|
{
|
|
|
|
|
return NaN;
|
|
|
|
|
}
|
|
|
|
|
DoubleDouble larger = Abs(x);
|
|
|
|
|
DoubleDouble smaller = Abs(y);
|
|
|
|
|
if (larger < smaller)
|
|
|
|
|
{
|
|
|
|
|
(larger, smaller) = (smaller, larger);
|
|
|
|
|
}
|
|
|
|
|
if (IsZero(smaller))
|
|
|
|
|
{
|
|
|
|
|
return larger;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int exponent = Math.ILogB(larger.High);
|
|
|
|
|
if (exponent - Math.ILogB(smaller.High) > 54)
|
|
|
|
|
{
|
|
|
|
|
// r=smaller/larger < 2^-54. sqrt(1+r²)=1+r²/2+O(r^4).
|
|
|
|
|
// Do not square r: its square can underflow even when the final
|
|
|
|
|
// low correction is representable (e.g. larger=2^1000, smaller=2^450).
|
|
|
|
|
// In the top binade this correction is <2^914, smaller than the
|
|
|
|
|
// minimum 2^917 gap between any canonical finite value and overflow.
|
|
|
|
|
return larger + ((smaller / larger) * (smaller * 0.5));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DoubleDouble a = FromComponents(Math.ScaleB(larger.High, -exponent), Math.ScaleB(larger.Low, -exponent));
|
|
|
|
|
DoubleDouble b = FromComponents(Math.ScaleB(smaller.High, -exponent), Math.ScaleB(smaller.Low, -exponent));
|
|
|
|
|
DoubleDouble root = Sqrt((a * a) + (b * b));
|
|
|
|
|
// root is finite and in [1, 3), with total exponent in [-1074, 1024].
|
|
|
|
|
// The boundary helper rounds the complete pair, not high before low.
|
|
|
|
|
if (exponent >= -969 && exponent <= 1021)
|
|
|
|
|
{
|
|
|
|
|
return FromComponents(Math.ScaleB(root.High, exponent), Math.ScaleB(root.Low, exponent));
|
|
|
|
|
}
|
|
|
|
|
if (exponent == 1023)
|
|
|
|
|
{
|
|
|
|
|
return HypotOverflowBoundary(larger, smaller, Zero, root);
|
|
|
|
|
}
|
|
|
|
|
return PreciseMathHelper.ScalePowerOfTwoBoundary(root, exponent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Returns the three-dimensional Euclidean norm sqrt(x² + y² + z²).</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// Uses bounded scaled squares rather than unscaled products or nested norms.
|
|
|
|
|
/// Any infinity takes precedence over NaN; otherwise NaN propagates. Results
|
|
|
|
|
/// are nonnegative, including positive zero. A zero coordinate reduces to the
|
|
|
|
|
/// two-argument overload. Overflow classification uses exact squared inputs.
|
|
|
|
|
/// Approximate finite results are tested against 2^-100 relative error plus
|
|
|
|
|
/// one minimum binary64 subnormal, not guaranteed correctly rounded.
|
|
|
|
|
/// This convenience overload is not part of IRootFunctions.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
[Pure]
|
|
|
|
|
public static DoubleDouble Hypot(DoubleDouble x, DoubleDouble y, DoubleDouble z)
|
|
|
|
|
{
|
|
|
|
|
if (IsInfinity(x) || IsInfinity(y) || IsInfinity(z))
|
|
|
|
|
{
|
|
|
|
|
return PositiveInfinity;
|
|
|
|
|
}
|
|
|
|
|
if (IsNaN(x) || IsNaN(y) || IsNaN(z))
|
|
|
|
|
{
|
|
|
|
|
return NaN;
|
|
|
|
|
}
|
|
|
|
|
DoubleDouble largest = Abs(x);
|
|
|
|
|
DoubleDouble middle = Abs(y);
|
|
|
|
|
DoubleDouble smallest = Abs(z);
|
|
|
|
|
// A fixed magnitude order gives the same evaluation for every permutation.
|
|
|
|
|
if (largest < middle)
|
|
|
|
|
{
|
|
|
|
|
(largest, middle) = (middle, largest);
|
|
|
|
|
}
|
|
|
|
|
if (middle < smallest)
|
|
|
|
|
{
|
|
|
|
|
(middle, smallest) = (smallest, middle);
|
|
|
|
|
}
|
|
|
|
|
if (largest < middle)
|
|
|
|
|
{
|
|
|
|
|
(largest, middle) = (middle, largest);
|
|
|
|
|
}
|
|
|
|
|
if (IsZero(smallest))
|
|
|
|
|
{
|
|
|
|
|
return Hypot(largest, middle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int exponent = Math.ILogB(largest.High);
|
|
|
|
|
if (exponent - Math.ILogB(middle.High) > 54)
|
|
|
|
|
{
|
|
|
|
|
// For r=middle/largest and s=smallest/largest, use
|
|
|
|
|
// sqrt(1+r²+s²)=1+(r²+s²)/2+O((r²+s²)²). Avoid squaring
|
|
|
|
|
// tiny ratios and combine both corrections BEFORE halving: each
|
|
|
|
|
// half can underflow even when their sum is representable.
|
|
|
|
|
// In the top binade the total correction is <2^915, below the
|
|
|
|
|
// minimum 2^917 distance of a canonical finite input from overflow.
|
|
|
|
|
DoubleDouble correction = ((middle / largest) * middle) + ((smallest / largest) * smallest);
|
|
|
|
|
return largest + (correction * 0.5);
|
|
|
|
|
}
|
|
|
|
|
DoubleDouble a = FromComponents(Math.ScaleB(largest.High, -exponent), Math.ScaleB(largest.Low, -exponent));
|
|
|
|
|
DoubleDouble b = FromComponents(Math.ScaleB(middle.High, -exponent), Math.ScaleB(middle.Low, -exponent));
|
|
|
|
|
DoubleDouble c = FromComponents(Math.ScaleB(smallest.High, -exponent), Math.ScaleB(smallest.Low, -exponent));
|
|
|
|
|
// Sum the smaller squares first. All scaled highs are at most two,
|
|
|
|
|
// so the root stays in [1, 4) and cannot overflow before final rescaling.
|
|
|
|
|
DoubleDouble root = Sqrt((a * a) + ((b * b) + (c * c)));
|
|
|
|
|
if (exponent >= -969 && exponent <= 1021)
|
|
|
|
|
{
|
|
|
|
|
return FromComponents(Math.ScaleB(root.High, exponent), Math.ScaleB(root.Low, exponent));
|
|
|
|
|
}
|
|
|
|
|
if (exponent == 1023)
|
|
|
|
|
{
|
|
|
|
|
return HypotOverflowBoundary(largest, middle, smallest, root);
|
|
|
|
|
}
|
|
|
|
|
return PreciseMathHelper.ScalePowerOfTwoBoundary(root, exponent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Returns the real nth root, or its reciprocal for negative n.</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// Supports every int degree, including int.MinValue. Degree zero and even
|
|
|
|
|
/// roots of negative nonzero values return canonical NaN. Odd degrees retain
|
|
|
|
|
/// the input sign; even degrees map either zero to positive zero (or positive
|
|
|
|
|
/// infinity for negative degrees). Negative degrees exchange zeros/infinities.
|
|
|
|
|
/// Degree one preserves both components; minus one uses the reciprocal kernel.
|
|
|
|
|
/// For |n| >= 2, finite nonzero results are tested against 2^-100 relative error,
|
|
|
|
|
/// not guaranteed correctly rounded. A binary64 seed is refined with scaled
|
|
|
|
|
/// integer powers, then an exponent-safe correction includes the input low.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
[Pure]
|
|
|
|
|
public static DoubleDouble RootN(DoubleDouble value, int n)
|
|
|
|
|
{
|
|
|
|
|
if (n == 0 || IsNaN(value))
|
|
|
|
|
{
|
|
|
|
|
return NaN;
|
|
|
|
|
}
|
|
|
|
|
bool odd = (n & 1) != 0;
|
|
|
|
|
if (value.High < 0.0 && !odd)
|
|
|
|
|
{
|
|
|
|
|
return NaN;
|
|
|
|
|
}
|
|
|
|
|
if (IsZero(value) || IsInfinity(value))
|
|
|
|
|
{
|
|
|
|
|
bool infinite = IsZero(value) == (n < 0);
|
|
|
|
|
double result = infinite ? double.PositiveInfinity : 0.0;
|
|
|
|
|
return new DoubleDouble(odd ? Math.CopySign(result, value.High) : result);
|
|
|
|
|
}
|
|
|
|
|
switch (n)
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
return value;
|
|
|
|
|
case -1:
|
|
|
|
|
return DDMath.Reciprocal(value);
|
|
|
|
|
case 2:
|
|
|
|
|
return Sqrt(value);
|
|
|
|
|
case -2:
|
|
|
|
|
return InvSqrt(value);
|
|
|
|
|
case 3:
|
|
|
|
|
return Cbrt(value);
|
|
|
|
|
case -3:
|
|
|
|
|
return 1.0 / Cbrt(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DoubleDouble magnitude = Abs(value);
|
|
|
|
|
// Widen before negation; |int.MinValue| is not representable as int.
|
|
|
|
|
long degree = n < 0 ? -(long)n : n;
|
|
|
|
|
int exponent = Math.ILogB(magnitude.High);
|
|
|
|
|
DoubleDouble root;
|
|
|
|
|
if (Math.ScaleB(magnitude.High, -exponent) == 1.0 && exponent % degree == 0)
|
|
|
|
|
{
|
|
|
|
|
// Preserve exact binary power roots without iterative roundoff.
|
|
|
|
|
root = new DoubleDouble(Math.ScaleB(1.0, (int)(exponent / degree)));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
root = new DoubleDouble(Math.Exp(Math.Log(magnitude.High) / degree));
|
|
|
|
|
// r += r*(high/r^degree - 1)/degree. Track the power exponent
|
|
|
|
|
// separately: r^degree can cross a range boundary even though r
|
|
|
|
|
// and the Newton correction are finite. Three refinements also
|
|
|
|
|
// cover the degree-amplified seed error at the largest int degrees.
|
|
|
|
|
for (int iteration = 0; iteration < 3; ++iteration)
|
|
|
|
|
{
|
|
|
|
|
DoubleDouble residual = RootPowerRatio(magnitude.High, root, degree) - 1.0;
|
|
|
|
|
root += (root * residual) / (double)degree;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (n < 0)
|
|
|
|
|
{
|
|
|
|
|
root = 1.0 / root;
|
|
|
|
|
}
|
|
|
|
|
root += RootLowCorrection(magnitude, root, n);
|
|
|
|
|
return value.High < 0.0 ? -root : root;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Inputs are nonnegative finite and the largest high has exponent 1023.
|
|
|
|
|
// The two-dimensional overload supplies zero for z.
|
|
|
|
|
// The scaled root is approximate: it can round to the overflow midpoint
|
|
|
|
|
// from below. Classify x²+y²+z² against T² exactly before rescaling; T is the
|
|
|
|
|
// binary64 overflow midpoint 2^1024-2^970. A midpoint itself overflows.
|
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
|
|
|
private static DoubleDouble HypotOverflowBoundary(DoubleDouble x, DoubleDouble y, DoubleDouble z, DoubleDouble scaledRoot)
|
|
|
|
|
{
|
|
|
|
|
BigInteger a = PreciseMathHelper.ArithmeticUnits(x);
|
|
|
|
|
BigInteger b = PreciseMathHelper.ArithmeticUnits(y);
|
|
|
|
|
BigInteger c = PreciseMathHelper.ArithmeticUnits(z);
|
|
|
|
|
BigInteger threshold = PreciseMathHelper.ArithmeticUnits(double.MaxValue)
|
|
|
|
|
+ PreciseMathHelper.ArithmeticUnits(Math.ScaleB(1.0, 970));
|
|
|
|
|
if ((a * a) + (b * b) + (c * c) >= threshold * threshold)
|
|
|
|
|
{
|
|
|
|
|
return PositiveInfinity;
|
|
|
|
|
}
|
|
|
|
|
DoubleDouble result = PreciseMathHelper.ScalePowerOfTwoBoundary(scaledRoot, 1023);
|
|
|
|
|
// If only the approximation overflowed, clamp to the largest canonical
|
|
|
|
|
// finite pair. Its distance below T is 2^917 (<2^-106 relative), within
|
|
|
|
|
// the approximate error budget; do not claim correctly rounded lows.
|
|
|
|
|
return IsInfinity(result)
|
|
|
|
|
? new DoubleDouble(double.MaxValue, Math.BitDecrement(Math.ScaleB(1.0, 970)))
|
|
|
|
|
: result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// high is positive finite, degree is in [4, 2^31], and root is a nearby
|
|
|
|
|
// positive nth-root estimate. The resulting ratio is near one. Intermediate
|
|
|
|
|
// mantissas stay in [1, 4] before normalization, with wide tracked exponents.
|
|
|
|
|
private static DoubleDouble RootPowerRatio(double high, DoubleDouble root, long degree)
|
|
|
|
|
{
|
|
|
|
|
long factorExponent = Math.ILogB(root.High);
|
|
|
|
|
DoubleDouble factor = FromComponents(Math.ScaleB(root.High, -(int)factorExponent),
|
|
|
|
|
Math.ScaleB(root.Low, -(int)factorExponent));
|
|
|
|
|
DoubleDouble power = One;
|
|
|
|
|
long powerExponent = 0;
|
|
|
|
|
while (degree != 0)
|
|
|
|
|
{
|
|
|
|
|
if ((degree & 1) != 0)
|
|
|
|
|
{
|
|
|
|
|
power *= factor;
|
|
|
|
|
powerExponent += factorExponent;
|
|
|
|
|
power = RootNormalizeMantissa(power, ref powerExponent);
|
|
|
|
|
}
|
|
|
|
|
degree >>= 1;
|
|
|
|
|
if (degree != 0)
|
|
|
|
|
{
|
|
|
|
|
factor *= factor;
|
|
|
|
|
factorExponent *= 2;
|
|
|
|
|
factor = RootNormalizeMantissa(factor, ref factorExponent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// The seed/refinements keep powerExponent close to ILogB(high), so
|
|
|
|
|
// scaling high into the power's binade is bounded and normal.
|
|
|
|
|
return new DoubleDouble(Math.ScaleB(high, -(int)powerExponent)) / power;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static DoubleDouble RootNormalizeMantissa(DoubleDouble value, ref long exponent)
|
|
|
|
|
{
|
|
|
|
|
int adjustment = Math.ILogB(value.High);
|
|
|
|
|
exponent += adjustment;
|
|
|
|
|
return FromComponents(Math.ScaleB(value.High, -adjustment), Math.ScaleB(value.Low, -adjustment));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// value is positive finite, root approximates value.High^(1/n), |n| >= 2.
|
|
|
|
|
// Apply low*root/(n*high) using bounded mantissas and separate exponents.
|
|
|
|
|
// Forming low/high first can underflow before root rescales the correction.
|
|
|
|
|
// Normalization bounds |low/high| by 2^-53: omitted quadratic terms are
|
|
|
|
|
// below 2^-106 relative, within the tested 2^-100 root error budget. Since
|
|
|
|
|
// these roots are normal and bounded by 2^537, the correction cannot overflow.
|
|
|
|
|
private static DoubleDouble RootLowCorrection(DoubleDouble value, DoubleDouble root, int n)
|
|
|
|
|
{
|
|
|
|
|
if (value.Low == 0.0)
|
|
|
|
|
{
|
|
|
|
|
return Zero;
|
|
|
|
|
}
|
|
|
|
|
int highExponent = Math.ILogB(value.High);
|
|
|
|
|
int lowExponent = Math.ILogB(Math.Abs(value.Low));
|
|
|
|
|
int rootExponent = Math.ILogB(root.High);
|
|
|
|
|
double high = Math.ScaleB(value.High, -highExponent);
|
|
|
|
|
double low = Math.ScaleB(value.Low, -lowExponent);
|
|
|
|
|
DoubleDouble scaledRoot = FromComponents(Math.ScaleB(root.High, -rootExponent), Math.ScaleB(root.Low, -rootExponent));
|
|
|
|
|
DoubleDouble correction = (scaledRoot * low) / (new DoubleDouble(high) * n);
|
|
|
|
|
int exponent = lowExponent - highExponent + rootExponent;
|
|
|
|
|
int resultExponent = Math.ILogB(Math.Abs(correction.High)) + exponent;
|
|
|
|
|
if (resultExponent < -1075)
|
|
|
|
|
{
|
|
|
|
|
return Zero;
|
|
|
|
|
}
|
|
|
|
|
if (resultExponent < -969)
|
|
|
|
|
{
|
|
|
|
|
return PreciseMathHelper.ScalePowerOfTwoBoundary(correction, exponent);
|
|
|
|
|
}
|
|
|
|
|
return FromComponents(Math.ScaleB(correction.High, exponent), Math.ScaleB(correction.Low, exponent));
|
|
|
|
|
}
|
|
|
|
|
}
|