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:
@@ -6,25 +6,81 @@ namespace Just.PreciseMath.Tests;
|
||||
public class DoubleDoubleTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("PI", 3.141592653589793, 1.2246467991473532e-16)]
|
||||
[InlineData("Pi", 3.141592653589793, 1.2246467991473532e-16)]
|
||||
[InlineData("E", 2.718281828459045, 1.4456468917292502e-16)]
|
||||
[InlineData("LN2", 0.6931471805599453, 2.3190468138462996e-17)]
|
||||
[InlineData("Ln2", 0.6931471805599453, 2.3190468138462996e-17)]
|
||||
[InlineData("Tau", 6.283185307179586, 2.4492935982947064e-16)]
|
||||
[InlineData("PiOver2", 1.5707963267948966, 6.123233995736766e-17)]
|
||||
[InlineData("PiOver3", 1.0471975511965979, -1.072081766451091e-16)]
|
||||
[InlineData("PiOver4", 0.7853981633974483, 3.061616997868383e-17)]
|
||||
[InlineData("PiOver6", 0.5235987755982989, -5.360408832255455e-17)]
|
||||
[InlineData("InvPi", 0.3183098861837907, -1.9678676675182486e-17)]
|
||||
[InlineData("InvTau", 0.15915494309189535, -9.839338337591243e-18)]
|
||||
[InlineData("DegToRad", 0.017453292519943295, 2.9486522708701687e-19)]
|
||||
[InlineData("RadToDeg", 57.29577951308232, -1.9878495670576283e-15)]
|
||||
[InlineData("InvE", 0.36787944117144233, -1.2428753672788363e-17)]
|
||||
[InlineData("Ln10", 2.302585092994046, -2.1707562233822494e-16)]
|
||||
[InlineData("Log2E", 1.4426950408889634, 2.0355273740931033e-17)]
|
||||
[InlineData("Log10E", 0.4342944819032518, 1.098319650216765e-17)]
|
||||
[InlineData("Log2Of10", 3.321928094887362, 1.661617516973592e-16)]
|
||||
[InlineData("Log10Of2", 0.3010299956639812, -2.8037281277851704e-18)]
|
||||
[InlineData("Sqrt2", 1.4142135623730951, -9.667293313452913e-17)]
|
||||
[InlineData("Sqrt3", 1.7320508075688772, 1.0035084221806903e-16)]
|
||||
[InlineData("Sqrt5", 2.23606797749979, -1.0864230407365012e-16)]
|
||||
[InlineData("InvSqrt2", 0.7071067811865476, -4.833646656726457e-17)]
|
||||
[InlineData("InvSqrt3", 0.5773502691896257, 3.3450280739356345e-17)]
|
||||
[InlineData("SqrtPi", 1.772453850905516, -7.666586499825799e-17)]
|
||||
[InlineData("InvSqrtPi", 0.5641895835477563, 7.66772980658294e-18)]
|
||||
[InlineData("TwoInvSqrtPi", 1.1283791670955126, 1.533545961316588e-17)]
|
||||
[InlineData("SqrtTau", 2.5066282746310007, -1.8328579980459167e-16)]
|
||||
[InlineData("InvSqrtTau", 0.3989422804014327, -2.49232720227773e-17)]
|
||||
[InlineData("GoldenRatio", 1.618033988749895, -5.432115203682506e-17)]
|
||||
public void ConstantsHaveNearestBinary64Residuals(string name, double high, double low)
|
||||
{
|
||||
// Each residual is round_binary64(constant - exact_binary64(high)).
|
||||
// Reproduced with Python decimal at precision 90: e = Decimal(1).exp(),
|
||||
// ln(2) = Decimal(2).ln(), pi = 16*atan(1/5) - 4*atan(1/239), using
|
||||
// atan(x) = sum((-1)^k*x^(2k+1)/(2k+1)) until |term| < 1e-95.
|
||||
// low = float(reference - Decimal.from_float(float(reference))).
|
||||
// Reproduce with ReferenceData/generate_constants.py: Python Decimal at
|
||||
// 160 and 240 digits, Machin's pi, exp/ln/sqrt and exact Fraction splitting.
|
||||
// The generator records every formula and checks normalized components.
|
||||
DoubleDouble value = name switch
|
||||
{
|
||||
"PI" => DoubleDouble.PI,
|
||||
"Pi" => DoubleDouble.Pi,
|
||||
"E" => DoubleDouble.E,
|
||||
"LN2" => DoubleDouble.LN2,
|
||||
"Ln2" => DoubleDouble.Ln2,
|
||||
"Tau" => DoubleDouble.Tau,
|
||||
"PiOver2" => DoubleDouble.PiOver2,
|
||||
"PiOver3" => DoubleDouble.PiOver3,
|
||||
"PiOver4" => DoubleDouble.PiOver4,
|
||||
"PiOver6" => DoubleDouble.PiOver6,
|
||||
"InvPi" => DoubleDouble.InvPi,
|
||||
"InvTau" => DoubleDouble.InvTau,
|
||||
"DegToRad" => DoubleDouble.DegToRad,
|
||||
"RadToDeg" => DoubleDouble.RadToDeg,
|
||||
"InvE" => DoubleDouble.InvE,
|
||||
"Ln10" => DoubleDouble.Ln10,
|
||||
"Log2E" => DoubleDouble.Log2E,
|
||||
"Log10E" => DoubleDouble.Log10E,
|
||||
"Log2Of10" => DoubleDouble.Log2Of10,
|
||||
"Log10Of2" => DoubleDouble.Log10Of2,
|
||||
"Sqrt2" => DoubleDouble.Sqrt2,
|
||||
"Sqrt3" => DoubleDouble.Sqrt3,
|
||||
"Sqrt5" => DoubleDouble.Sqrt5,
|
||||
"InvSqrt2" => DoubleDouble.InvSqrt2,
|
||||
"InvSqrt3" => DoubleDouble.InvSqrt3,
|
||||
"SqrtPi" => DoubleDouble.SqrtPi,
|
||||
"InvSqrtPi" => DoubleDouble.InvSqrtPi,
|
||||
"TwoInvSqrtPi" => DoubleDouble.TwoInvSqrtPi,
|
||||
"SqrtTau" => DoubleDouble.SqrtTau,
|
||||
"InvSqrtTau" => DoubleDouble.InvSqrtTau,
|
||||
"GoldenRatio" => DoubleDouble.GoldenRatio,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(name)),
|
||||
};
|
||||
value.High.ShouldBe(high);
|
||||
value.Low.ShouldBe(low);
|
||||
BitConverter.DoubleToInt64Bits(value.High).ShouldBe(BitConverter.DoubleToInt64Bits(high));
|
||||
BitConverter.DoubleToInt64Bits(value.Low).ShouldBe(BitConverter.DoubleToInt64Bits(low));
|
||||
DoubleDouble.IsCanonical(value).ShouldBeTrue();
|
||||
DoubleDouble.IsFinite(value).ShouldBeTrue();
|
||||
value.Low.ShouldNotBe(0.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -46,7 +102,7 @@ public class DoubleDoubleTests
|
||||
[InlineData(3.141592653589793, 1.2246467991473532e-16)]
|
||||
[InlineData(2.718281828459045, 1.4456468917292502e-16)]
|
||||
[InlineData(0.6931471805599453, 2.3190468138462996e-17)]
|
||||
public void AdditiveIdentity(double high, double low)
|
||||
public void AdditiveIdentityAdd(double high, double low)
|
||||
{
|
||||
DoubleDouble value = new(high, low);
|
||||
|
||||
@@ -70,7 +126,32 @@ public class DoubleDoubleTests
|
||||
[InlineData(3.141592653589793, 1.2246467991473532e-16)]
|
||||
[InlineData(2.718281828459045, 1.4456468917292502e-16)]
|
||||
[InlineData(0.6931471805599453, 2.3190468138462996e-17)]
|
||||
public void MultiplicativeIdentity(double high, double low)
|
||||
public void AdditiveIdentitySubtract(double high, double low)
|
||||
{
|
||||
DoubleDouble value = new(high, low);
|
||||
DoubleDouble negativeValue = -value;
|
||||
|
||||
DoubleDouble result = value - DoubleDouble.AdditiveIdentity;
|
||||
DoubleDouble resultInversedOrder = DoubleDouble.AdditiveIdentity - value;
|
||||
|
||||
result.High.ShouldBe(high);
|
||||
result.Low.ShouldBe(low);
|
||||
|
||||
resultInversedOrder.High.ShouldBe(negativeValue.High);
|
||||
resultInversedOrder.Low.ShouldBe(negativeValue.Low);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1.0, 0.0)]
|
||||
[InlineData(10.0, 0.0)]
|
||||
[InlineData(100.0, 0.0)]
|
||||
[InlineData(-1.0, 0.0)]
|
||||
[InlineData(-10.0, 0.0)]
|
||||
[InlineData(-100.0, 0.0)]
|
||||
[InlineData(3.141592653589793, 1.2246467991473532e-16)]
|
||||
[InlineData(2.718281828459045, 1.4456468917292502e-16)]
|
||||
[InlineData(0.6931471805599453, 2.3190468138462996e-17)]
|
||||
public void MultiplicativeIdentityMultiply(double high, double low)
|
||||
{
|
||||
DoubleDouble value = new(high, low);
|
||||
|
||||
@@ -83,4 +164,24 @@ public class DoubleDoubleTests
|
||||
resultInversedOrder.High.ShouldBe(high);
|
||||
resultInversedOrder.Low.ShouldBe(low);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1.0, 0.0)]
|
||||
[InlineData(10.0, 0.0)]
|
||||
[InlineData(100.0, 0.0)]
|
||||
[InlineData(-1.0, 0.0)]
|
||||
[InlineData(-10.0, 0.0)]
|
||||
[InlineData(-100.0, 0.0)]
|
||||
[InlineData(3.141592653589793, 1.2246467991473532e-16)]
|
||||
[InlineData(2.718281828459045, 1.4456468917292502e-16)]
|
||||
[InlineData(0.6931471805599453, 2.3190468138462996e-17)]
|
||||
public void MultiplicativeIdentityDivide(double high, double low)
|
||||
{
|
||||
DoubleDouble value = new(high, low);
|
||||
|
||||
DoubleDouble result = value / DoubleDouble.MultiplicativeIdentity;
|
||||
|
||||
result.High.ShouldBe(high);
|
||||
result.Low.ShouldBe(low);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user