added root functions
.NET Test / .NET tests (push) Successful in 1m31s

This commit is contained in:
2026-09-16 20:24:08 +04:00
parent 24d2aef892
commit 7084fae174
11 changed files with 1912 additions and 94 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ public static partial class DDMath
if (exponent < 0)
{
// Reciprocate the bounded significand, NOT the range-limited result.
result = DoubleDouble.One / result;
result = 1.0 / result;
resultExponent = -resultExponent;
}
return ScalePowerOfTwo(negative ? -result : result, resultExponent);
+32 -86
View File
@@ -48,100 +48,46 @@ public static partial class DDMath
return new DoubleDouble(high, low == 0.0 ? 0.0 : low);
}
/// <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>
/// <inheritdoc cref="DoubleDouble.Cbrt"/>
[Pure]
public static DoubleDouble Cbrt(DoubleDouble value)
{
return DoubleDouble.Cbrt(value);
}
/// <inheritdoc cref="DoubleDouble.Hypot(DoubleDouble, DoubleDouble)"/>
[Pure]
public static DoubleDouble Hypot(DoubleDouble x, DoubleDouble y)
{
return DoubleDouble.Hypot(x, y);
}
/// <inheritdoc cref="DoubleDouble.Hypot(DoubleDouble, DoubleDouble, DoubleDouble)"/>
[Pure]
public static DoubleDouble Hypot(DoubleDouble x, DoubleDouble y, DoubleDouble z)
{
return DoubleDouble.Hypot(x, y, z);
}
/// <inheritdoc cref="DoubleDouble.RootN"/>
[Pure]
public static DoubleDouble RootN(DoubleDouble value, int n)
{
return DoubleDouble.RootN(value, n);
}
/// <inheritdoc cref="DoubleDouble.Sqrt"/>
[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));
return DoubleDouble.Sqrt(value);
}
/// <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>
/// <inheritdoc cref="DoubleDouble.InvSqrt"/>
[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));
return DoubleDouble.InvSqrt(value);
}
// For finite, nonzero normalized significands and exponents bounded by the
@@ -62,13 +62,13 @@ public readonly partial struct DoubleDouble
/// <summary>Adds one using double-double arithmetic, including its overflow and nonfinite behavior.</summary>
public static DoubleDouble operator ++(DoubleDouble value)
{
return value + One;
return value + 1.0;
}
/// <summary>Subtracts one using double-double arithmetic, including its overflow and nonfinite behavior.</summary>
public static DoubleDouble operator --(DoubleDouble value)
{
return value - One;
return value - 1.0;
}
/// <summary>Tests normalization and canonical NaN and zero-residual bit patterns.</summary>
@@ -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));
}
}