add precomputed mathematical constants and specialize the second division residual
.NET Test / .NET tests (push) Successful in 2m23s
.NET Test / .NET tests (push) Successful in 2m23s
This commit is contained in:
@@ -101,9 +101,13 @@ public readonly partial struct DoubleDouble :
|
||||
double quotient = left._high / right._high;
|
||||
DoubleDouble remainder = left - (right * quotient);
|
||||
double correction = remainder._high / right._high;
|
||||
remainder -= right * correction;
|
||||
double finalCorrection = remainder._high / right._high;
|
||||
return FromComponents(quotient, correction) + finalCorrection;
|
||||
double finalRemainder = PreciseMathHelper.SubtractDivisionCorrectionHigh(remainder, right * correction);
|
||||
double finalCorrection = finalRemainder / right._high;
|
||||
// The entry guard gives |quotient| <= 2^901. Conservatively bounding
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <summary>Applies the expansion operation without discarding the low component.</summary>
|
||||
|
||||
@@ -98,10 +98,15 @@ public readonly partial struct DoubleDouble :
|
||||
/// Represents a zero value.
|
||||
/// </summary>
|
||||
public static DoubleDouble Zero => new();
|
||||
|
||||
// Mathematical constants store normalized, precomputed binary64 pairs: the
|
||||
// nearest high and then the nearest residual of the high-precision value.
|
||||
// No DD arithmetic, parsing, normalization or heap allocation occurs on access.
|
||||
// Reproduce with the tests' ReferenceData/generate_constants.py.
|
||||
/// <summary>
|
||||
/// Represents the ratio of the circumference of a circle to its diameter, specified by the constant, π.
|
||||
/// </summary>
|
||||
public static DoubleDouble PI => new(3.141592653589793, 1.2246467991473532e-16);
|
||||
public static DoubleDouble Pi => new(3.141592653589793, 1.2246467991473532e-16);
|
||||
/// <summary>
|
||||
/// Represents the natural logarithmic base, specified by the constant, e.
|
||||
/// </summary>
|
||||
@@ -109,7 +114,85 @@ public readonly partial struct DoubleDouble :
|
||||
/// <summary>
|
||||
/// Represents the natural logarithm of value 2.
|
||||
/// </summary>
|
||||
public static DoubleDouble LN2 => new(0.6931471805599453, 2.3190468138462996e-17);
|
||||
public static DoubleDouble Ln2 => new(0.6931471805599453, 2.3190468138462996e-17);
|
||||
|
||||
/// <summary>Gets τ = 2π, the angle of one full turn in radians.</summary>
|
||||
public static DoubleDouble Tau => new(6.283185307179586, 2.4492935982947064e-16);
|
||||
|
||||
/// <summary>Gets π/2, the angle of 90 degrees in radians.</summary>
|
||||
public static DoubleDouble PiOver2 => new(1.5707963267948966, 6.123233995736766e-17);
|
||||
|
||||
/// <summary>Gets π/3, the angle of 60 degrees in radians.</summary>
|
||||
public static DoubleDouble PiOver3 => new(1.0471975511965979, -1.072081766451091e-16);
|
||||
|
||||
/// <summary>Gets π/4, the angle of 45 degrees in radians.</summary>
|
||||
public static DoubleDouble PiOver4 => new(0.7853981633974483, 3.061616997868383e-17);
|
||||
|
||||
/// <summary>Gets π/6, the angle of 30 degrees in radians.</summary>
|
||||
public static DoubleDouble PiOver6 => new(0.5235987755982989, -5.360408832255455e-17);
|
||||
|
||||
/// <summary>Gets 1/π.</summary>
|
||||
public static DoubleDouble InvPi => new(0.3183098861837907, -1.9678676675182486e-17);
|
||||
|
||||
/// <summary>Gets 1/(2π), the factor for converting radians to turns.</summary>
|
||||
public static DoubleDouble InvTau => new(0.15915494309189535, -9.839338337591243e-18);
|
||||
|
||||
/// <summary>Gets π/180. Multiply an angle in degrees by this value to obtain radians.</summary>
|
||||
public static DoubleDouble DegToRad => new(0.017453292519943295, 2.9486522708701687e-19);
|
||||
|
||||
/// <summary>Gets 180/π. Multiply an angle in radians by this value to obtain degrees.</summary>
|
||||
public static DoubleDouble RadToDeg => new(57.29577951308232, -1.9878495670576283e-15);
|
||||
|
||||
/// <summary>Gets 1/e = exp(-1).</summary>
|
||||
public static DoubleDouble InvE => new(0.36787944117144233, -1.2428753672788363e-17);
|
||||
|
||||
/// <summary>Gets ln(10), the factor for converting base-10 logarithms to natural logarithms.</summary>
|
||||
public static DoubleDouble Ln10 => new(2.302585092994046, -2.1707562233822494e-16);
|
||||
|
||||
/// <summary>Gets log₂(e) = 1/ln(2), the factor for converting natural logarithms to base 2.</summary>
|
||||
public static DoubleDouble Log2E => new(1.4426950408889634, 2.0355273740931033e-17);
|
||||
|
||||
/// <summary>Gets log₁₀(e) = 1/ln(10), the factor for converting natural logarithms to base 10.</summary>
|
||||
public static DoubleDouble Log10E => new(0.4342944819032518, 1.098319650216765e-17);
|
||||
|
||||
/// <summary>Gets log₂(10), the factor for converting base-10 logarithms to base 2.</summary>
|
||||
public static DoubleDouble Log2Of10 => new(3.321928094887362, 1.661617516973592e-16);
|
||||
|
||||
/// <summary>Gets log₁₀(2), the factor for converting base-2 logarithms to base 10.</summary>
|
||||
public static DoubleDouble Log10Of2 => new(0.3010299956639812, -2.8037281277851704e-18);
|
||||
|
||||
/// <summary>Gets √2.</summary>
|
||||
public static DoubleDouble Sqrt2 => new(1.4142135623730951, -9.667293313452913e-17);
|
||||
|
||||
/// <summary>Gets √3.</summary>
|
||||
public static DoubleDouble Sqrt3 => new(1.7320508075688772, 1.0035084221806903e-16);
|
||||
|
||||
/// <summary>Gets √5.</summary>
|
||||
public static DoubleDouble Sqrt5 => new(2.23606797749979, -1.0864230407365012e-16);
|
||||
|
||||
/// <summary>Gets 1/√2, also the sine and cosine of π/4.</summary>
|
||||
public static DoubleDouble InvSqrt2 => new(0.7071067811865476, -4.833646656726457e-17);
|
||||
|
||||
/// <summary>Gets 1/√3, also the tangent of π/6.</summary>
|
||||
public static DoubleDouble InvSqrt3 => new(0.5773502691896257, 3.3450280739356345e-17);
|
||||
|
||||
/// <summary>Gets √π, the Gaussian integral over the real line for exp(-x²).</summary>
|
||||
public static DoubleDouble SqrtPi => new(1.772453850905516, -7.666586499825799e-17);
|
||||
|
||||
/// <summary>Gets 1/√π, a Gaussian normalization factor.</summary>
|
||||
public static DoubleDouble InvSqrtPi => new(0.5641895835477563, 7.66772980658294e-18);
|
||||
|
||||
/// <summary>Gets 2/√π, the normalization factor in the error-function integral.</summary>
|
||||
public static DoubleDouble TwoInvSqrtPi => new(1.1283791670955126, 1.533545961316588e-17);
|
||||
|
||||
/// <summary>Gets √(2π), used in Gaussian integrals and Stirling's approximation.</summary>
|
||||
public static DoubleDouble SqrtTau => new(2.5066282746310007, -1.8328579980459167e-16);
|
||||
|
||||
/// <summary>Gets 1/√(2π), the standard normal probability density's normalization factor.</summary>
|
||||
public static DoubleDouble InvSqrtTau => new(0.3989422804014327, -2.49232720227773e-17);
|
||||
|
||||
/// <summary>Gets the golden ratio φ = (1 + √5)/2.</summary>
|
||||
public static DoubleDouble GoldenRatio => new(1.618033988749895, -5.432115203682506e-17);
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -104,10 +104,35 @@ internal static class PreciseMathHelper
|
||||
return NormalizeFinite(sum, sumError + (middleError + lowError));
|
||||
}
|
||||
|
||||
// Only for DD 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).
|
||||
// Eliminate those two transforms, not the low-sum error or final normalization.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static double SubtractDivisionCorrectionHigh(DoubleDouble remainder, DoubleDouble product)
|
||||
{
|
||||
ulong remainderBits = BitConverter.DoubleToUInt64Bits(remainder._high);
|
||||
ulong productBits = BitConverter.DoubleToUInt64Bits(product._high);
|
||||
if ((remainderBits & 0x7ff0_0000_0000_0000UL) == 0
|
||||
|| ((remainderBits ^ productBits) & 0xfff0_0000_0000_0000UL) != 0)
|
||||
{
|
||||
return (remainder - product)._high;
|
||||
}
|
||||
|
||||
double high = remainder._high - product._high;
|
||||
(double low, double lowError) = TwoAdd(remainder._low, -product._low);
|
||||
(double sum, double sumError) = TwoAdd(high, low);
|
||||
double error = sumError + lowError;
|
||||
// Preserve NormalizeFinite's zero-low shortcut, including the high zero's
|
||||
// sign. The normalized low output is unused by the final quotient correction.
|
||||
return error == 0.0 ? sum : sum + error;
|
||||
}
|
||||
|
||||
// Both components and their rounded sum must be finite. Unlike QuickTwoSum,
|
||||
// this entry point permits either magnitude order, including cancellation.
|
||||
// AddFinite establishes these bounds; this helper does not validate them or
|
||||
// canonicalize NaN/infinity. Use DoubleDouble.FromComponents for arbitrary pairs.
|
||||
// AddFinite and DD division 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)]
|
||||
internal static DoubleDouble NormalizeFinite(double high, double low)
|
||||
|
||||
Reference in New Issue
Block a user