This commit is contained in:
@@ -147,7 +147,14 @@ public readonly partial struct DoubleDouble :
|
||||
// the first product and subtraction gives |remainder.High| <= 2^457
|
||||
// at the first correction, hence |correction| <= 2^908 and a finite sum
|
||||
// below 2^909 in magnitude. Retain normalization before the final add.
|
||||
return PreciseMathHelper.NormalizeFinite(quotient, correction) + finalCorrection;
|
||||
// The correlated correction product (including its exact fallback) has
|
||||
// |High| <= 2^460, giving |finalRemainder| <= 2^462 and |finalCorrection|
|
||||
// <= 2^912. Both final-add highs therefore pass the addition range guard.
|
||||
// More tightly, the first product differs from left.High by < 4u*|left.High|,
|
||||
// so |remainder.High| < 16u*|left.High| and |correction/quotient| < 32u
|
||||
// (u=2^-53, including underflow). The normalized quotient cannot be zero.
|
||||
DoubleDouble partial = PreciseMathHelper.NormalizeFinite(quotient, correction);
|
||||
return PreciseMathHelper.AddScalarFinite(partial._high, partial._low, finalCorrection);
|
||||
}
|
||||
|
||||
/// <summary>Applies the expansion operation without discarding the low component.</summary>
|
||||
@@ -231,8 +238,10 @@ public readonly partial struct DoubleDouble :
|
||||
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;
|
||||
// before adding the last, preserving the DD/DD evaluation order. Its high
|
||||
// is nonzero and < 2^909; |finalCorrection| <= 2^912 fits scalar addition.
|
||||
DoubleDouble partial = PreciseMathHelper.NormalizeFinite(quotient, correction);
|
||||
return PreciseMathHelper.AddScalarFinite(partial._high, partial._low, finalCorrection);
|
||||
}
|
||||
|
||||
/// <summary>Divides a scalar using a quotient estimate and two residual corrections.</summary>
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
namespace Just.PreciseMath;
|
||||
|
||||
public readonly partial struct DoubleDouble : IComparable<DoubleDouble>, IComparable,
|
||||
public readonly partial struct DoubleDouble :
|
||||
IEquatable<DoubleDouble>,
|
||||
IEqualityOperators<DoubleDouble, DoubleDouble, bool>,
|
||||
IComparable<DoubleDouble>, IComparable,
|
||||
IComparisonOperators<DoubleDouble, DoubleDouble, bool>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[Pure]
|
||||
public bool Equals(DoubleDouble other)
|
||||
{
|
||||
return _high.Equals(other._high) && _low.Equals(other._low);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests numerical equality; NaN operands are never equal.
|
||||
/// </summary>
|
||||
|
||||
@@ -285,13 +285,18 @@ public readonly partial struct DoubleDouble : IRootFunctions<DoubleDouble>
|
||||
// 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);
|
||||
// Scaled |low| <= 2^-51 and the rounded sqrt seed's squared error
|
||||
// is < 2^-49, so |correction| < 2^-48. Both inputs and their sum
|
||||
// are finite (sum < 4); retain magnitude-aware normalization.
|
||||
DoubleDouble root = PreciseMathHelper.NormalizeFinite(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;
|
||||
root = DoubleDouble.FromComponents(Math.ScaleB(root.High, rootExponent), Math.ScaleB(root.Low, rootExponent));
|
||||
// rootExponent is in [-537, 511]. With root.High < 4 and normalized
|
||||
// |root.Low| <= 2^-52, scaled components and their sum stay below 2^514.
|
||||
root = PreciseMathHelper.NormalizeFinite(Math.ScaleB(root.High, rootExponent), Math.ScaleB(root.Low, rootExponent));
|
||||
// In this branch |originalLow/originalHigh| < 2^-1020. The omitted
|
||||
// quadratic term is negligible even relative to the low correction.
|
||||
return separateLow ? root + RootLowCorrection(value, root, 2) : root;
|
||||
|
||||
@@ -81,12 +81,6 @@ public readonly partial struct DoubleDouble :
|
||||
public readonly double Low => _low;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Pure]
|
||||
public bool Equals(DoubleDouble other)
|
||||
{
|
||||
return _high.Equals(other._high) && _low.Equals(other._low);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
[Pure]
|
||||
public override bool Equals(object? obj)
|
||||
|
||||
@@ -118,8 +118,11 @@ internal static class PreciseMathHelper
|
||||
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;
|
||||
// DD/DD's guard-derived bounds apply, including exact correction products:
|
||||
// the normalized high is nonzero and < 2^909; |finalCorrection| <= 2^912.
|
||||
// Keep normalization before scalar addition, without repeating its dispatch.
|
||||
DoubleDouble partial = NormalizeFinite(quotient, correction);
|
||||
return AddScalarFinite(partial._high, partial._low, finalCorrection);
|
||||
}
|
||||
|
||||
// Only for division's second remainder: normalized finite inputs with highs
|
||||
@@ -149,7 +152,7 @@ internal static class PreciseMathHelper
|
||||
|
||||
// Both components and their rounded sum must be finite. Unlike QuickTwoSum,
|
||||
// this entry point permits either magnitude order, including cancellation.
|
||||
// AddFinite and DD division establish these bounds; this helper does not validate
|
||||
// Arithmetic and Sqrt callers establish these bounds; this helper does not validate
|
||||
// them or canonicalize NaN/infinity. Use DoubleDouble.FromComponents for arbitrary pairs.
|
||||
// Retain the high zero's sign when low is zero, as FromComponents does.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -181,6 +184,16 @@ internal static class PreciseMathHelper
|
||||
return AddScalarBoundary(high, low, value);
|
||||
}
|
||||
|
||||
return AddScalarFinite(high, low, value);
|
||||
}
|
||||
|
||||
// Normalized finite (high, low), with a negated zero low also allowed.
|
||||
// high and value must pass the addition range guard and not both be zero.
|
||||
// Public scalar addition checks these conditions; division establishes them
|
||||
// before its final correction. Preserve all three transforms and grouping.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static DoubleDouble AddScalarFinite(double high, double low, double value)
|
||||
{
|
||||
(double sum, double error) = TwoAdd(high, value);
|
||||
// Specialize AddFinite for a zero-low scalar: adding that zero to low
|
||||
// has no residual. Retain the rounding error when combining the high
|
||||
|
||||
Reference in New Issue
Block a user