@@ -36,16 +36,7 @@ public static partial class DDMath
|
||||
return PreciseMathHelper.DivideBoundary(one, value);
|
||||
}
|
||||
|
||||
double quotient = one / value._high;
|
||||
double remainder = Math.FusedMultiplyAdd(-quotient, value._high, one);
|
||||
remainder = Math.FusedMultiplyAdd(-quotient, value._low, remainder);
|
||||
double correction = remainder / value._high;
|
||||
// For normalized input, using the high denominator in the correction
|
||||
// adds only O(u^2) error, with u=2^-53. The denominator exponent guard
|
||||
// keeps the quotient normal and finite; it dominates the correction,
|
||||
// so QuickTwoSum is ordered and its sum remains finite.
|
||||
(double high, double low) = PreciseMathHelper.TwoQuickAdd(quotient, correction);
|
||||
return new DoubleDouble(high, low == 0.0 ? 0.0 : low);
|
||||
return PreciseMathHelper.DivideScalarFinite(one, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="DoubleDouble.Cbrt"/>
|
||||
|
||||
@@ -103,24 +103,28 @@ public readonly partial struct DoubleDouble :
|
||||
}
|
||||
|
||||
/// <summary>Applies the expansion operation without discarding the low component.</summary>
|
||||
/// <remarks>Preserves the component bits of addition with the equivalent zero-low scalar expansion.</remarks>
|
||||
public static DoubleDouble operator +(DoubleDouble left, double right)
|
||||
{
|
||||
return PreciseMathHelper.AddScalar(left._high, left._low, right);
|
||||
}
|
||||
|
||||
/// <summary>Applies the expansion operation without discarding the low component.</summary>
|
||||
/// <remarks>Preserves the component bits of addition with the equivalent zero-low scalar expansion.</remarks>
|
||||
public static DoubleDouble operator +(double left, DoubleDouble right)
|
||||
{
|
||||
return right + left;
|
||||
}
|
||||
|
||||
/// <summary>Applies the expansion operation without discarding the low component.</summary>
|
||||
/// <remarks>Preserves the component bits of subtraction with the equivalent zero-low scalar expansion.</remarks>
|
||||
public static DoubleDouble operator -(DoubleDouble left, double right)
|
||||
{
|
||||
return PreciseMathHelper.AddScalar(left._high, left._low, -right);
|
||||
}
|
||||
|
||||
/// <summary>Applies the expansion operation without discarding the low component.</summary>
|
||||
/// <remarks>Preserves the component bits of subtraction with the equivalent zero-low scalar expansion.</remarks>
|
||||
public static DoubleDouble operator -(double left, DoubleDouble right)
|
||||
{
|
||||
// Negate the components, not the result: exact cancellation must yield +0.
|
||||
@@ -153,7 +157,8 @@ public readonly partial struct DoubleDouble :
|
||||
return right * left;
|
||||
}
|
||||
|
||||
/// <summary>Applies the expansion operation without discarding the low component.</summary>
|
||||
/// <summary>Divides by a scalar using a quotient estimate and two residual corrections.</summary>
|
||||
/// <remarks>Preserves the component bits of division by the equivalent zero-low expansion.</remarks>
|
||||
public static DoubleDouble operator /(DoubleDouble left, double right)
|
||||
{
|
||||
if (!IsFinite(left) || !double.IsFinite(right) || left._high == 0.0 || right == 0.0)
|
||||
@@ -166,15 +171,24 @@ public readonly partial struct DoubleDouble :
|
||||
}
|
||||
|
||||
double quotient = left._high / right;
|
||||
double remainder = Math.FusedMultiplyAdd(-quotient, right, left._high);
|
||||
double correction = (remainder + left._low) / right;
|
||||
// The exponent guard keeps the quotient normal. The correction is
|
||||
// O(u * quotient), so QuickTwoSum is ordered; one correction gives O(u^2) error.
|
||||
(double high, double low) = PreciseMathHelper.TwoQuickAdd(quotient, correction);
|
||||
return new DoubleDouble(high, low == 0.0 ? 0.0 : low);
|
||||
// The initial product is near left.High, so the exponent guard makes
|
||||
// TwoMultiply exact. Keep the complete remainder, including its low.
|
||||
(double product, double productError) = PreciseMathHelper.TwoMultiply(right, quotient);
|
||||
DoubleDouble remainder = PreciseMathHelper.AddFinite(left._high, left._low, -product, -productError);
|
||||
double correction = remainder._high / right;
|
||||
(double correctionProduct, double correctionError) = PreciseMathHelper.TwoMultiply(right, correction);
|
||||
// This product can be subnormal. FMA rounds its residual just as the
|
||||
// exact boundary product does; normalize and canonicalize its zero low.
|
||||
DoubleDouble correctedProduct = PreciseMathHelper.NormalizeFinite(correctionProduct, correctionError);
|
||||
double finalRemainder = PreciseMathHelper.SubtractDivisionCorrectionHigh(remainder, correctedProduct);
|
||||
double finalCorrection = finalRemainder / right;
|
||||
// The same finite bounds as DD/DD apply. Normalize the first correction
|
||||
// before adding the last, preserving the DD/DD evaluation order.
|
||||
return PreciseMathHelper.NormalizeFinite(quotient, correction) + finalCorrection;
|
||||
}
|
||||
|
||||
/// <summary>Applies the expansion operation without discarding the low component.</summary>
|
||||
/// <summary>Divides a scalar using a quotient estimate and two residual corrections.</summary>
|
||||
/// <remarks>Preserves the component bits of division with the equivalent zero-low numerator.</remarks>
|
||||
public static DoubleDouble operator /(double left, DoubleDouble right)
|
||||
{
|
||||
if (!double.IsFinite(left) || !IsFinite(right) || left == 0.0 || right._high == 0.0)
|
||||
@@ -186,13 +200,6 @@ public readonly partial struct DoubleDouble :
|
||||
return PreciseMathHelper.DivideBoundary(left, right);
|
||||
}
|
||||
|
||||
double quotient = left / right._high;
|
||||
double remainder = Math.FusedMultiplyAdd(-quotient, right._high, left);
|
||||
remainder = Math.FusedMultiplyAdd(-quotient, right._low, remainder);
|
||||
double correction = remainder / right._high;
|
||||
// Using the high denominator in the correction adds only O(u^2) error.
|
||||
// As above, the guarded quotient dominates its correction in magnitude.
|
||||
(double high, double low) = PreciseMathHelper.TwoQuickAdd(quotient, correction);
|
||||
return new DoubleDouble(high, low == 0.0 ? 0.0 : low);
|
||||
return PreciseMathHelper.DivideScalarFinite(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,25 @@ internal static class PreciseMathHelper
|
||||
return NormalizeFinite(sum, sumError + (middleError + lowError));
|
||||
}
|
||||
|
||||
// Only for DD division's second remainder: normalized finite inputs with highs
|
||||
// Finite, nonzero numerator and normalized denominator, both with high
|
||||
// exponents in [-450, 450]. Shared with Reciprocal after its constant-numerator
|
||||
// checks. Do not promote left or dispatch to the DD/DD division operator.
|
||||
internal static DoubleDouble DivideScalarFinite(double left, DoubleDouble right)
|
||||
{
|
||||
double quotient = left / right._high;
|
||||
DoubleDouble product = right * quotient;
|
||||
// Preserve DD/DD's complete first remainder. A chained scalar FMA drops
|
||||
// residual bits needed by the second correction; retain the expansion
|
||||
// subtraction before computing the next quotient correction.
|
||||
DoubleDouble remainder = AddFinite(left, 0.0, -product._high, -product._low);
|
||||
double correction = remainder._high / right._high;
|
||||
double finalRemainder = SubtractDivisionCorrectionHigh(remainder, right * correction);
|
||||
double finalCorrection = finalRemainder / right._high;
|
||||
// DD/DD's guard-derived finite bounds and normalization order apply.
|
||||
return NormalizeFinite(quotient, correction) + finalCorrection;
|
||||
}
|
||||
|
||||
// Only for division's second remainder: normalized finite inputs with highs
|
||||
// bounded by 2^462, including the correction product's exact fallback results.
|
||||
// Same sign and normal binade imply Sterbenz-exact high subtraction. Its error
|
||||
// is +0; with canonical input lows, TwoAdd(+0, lowSum) is (lowSum, +0).
|
||||
@@ -164,11 +182,14 @@ internal static class PreciseMathHelper
|
||||
}
|
||||
|
||||
(double sum, double error) = TwoAdd(high, value);
|
||||
// Near high-component cancellation, Sterbenz makes the first sum exact,
|
||||
// so error is zero and this retains low exactly. Otherwise its rounding
|
||||
// contributes only O(u^2) relative error. The final TwoSum normalizes.
|
||||
(double result, double residual) = TwoAdd(sum, error + low);
|
||||
return new DoubleDouble(result, residual == 0.0 ? 0.0 : residual);
|
||||
// Specialize AddFinite for a zero-low scalar: adding that zero to low
|
||||
// has no residual. Retain the rounding error when combining the high
|
||||
// sum's error with low, then fold it into the final normalization.
|
||||
// TwoSum also absorbs a negated zero low from scalar-left subtraction.
|
||||
(double middle, double middleError) = TwoAdd(error, low);
|
||||
(double result, double residual) = TwoAdd(sum, middle);
|
||||
// The unchanged addition guard gives AddFinite's finite-sum bounds.
|
||||
return NormalizeFinite(result, residual + middleError);
|
||||
}
|
||||
|
||||
// Keep the complete BigInteger expressions out of ordinary arithmetic bodies,
|
||||
|
||||
Reference in New Issue
Block a user