188 lines
7.6 KiB
C#
188 lines
7.6 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace Just.PreciseMath.Tests;
|
|
|
|
public class DoubleDoubleTests
|
|
{
|
|
[Theory]
|
|
[InlineData("Pi", 3.141592653589793, 1.2246467991473532e-16)]
|
|
[InlineData("E", 2.718281828459045, 1.4456468917292502e-16)]
|
|
[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)).
|
|
// 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,
|
|
"E" => DoubleDouble.E,
|
|
"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]
|
|
public void OneHasExpectedComponents()
|
|
{
|
|
DoubleDouble value = DoubleDouble.One;
|
|
|
|
value.High.ShouldBe(1.0);
|
|
value.Low.ShouldBe(0.0);
|
|
}
|
|
|
|
[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 AdditiveIdentityAdd(double high, double low)
|
|
{
|
|
DoubleDouble value = new(high, low);
|
|
|
|
DoubleDouble result = value + DoubleDouble.AdditiveIdentity;
|
|
DoubleDouble resultInversedOrder = DoubleDouble.AdditiveIdentity + value;
|
|
|
|
result.High.ShouldBe(high);
|
|
result.Low.ShouldBe(low);
|
|
|
|
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 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);
|
|
|
|
DoubleDouble result = value * DoubleDouble.MultiplicativeIdentity;
|
|
DoubleDouble resultInversedOrder = DoubleDouble.MultiplicativeIdentity * value;
|
|
|
|
result.High.ShouldBe(high);
|
|
result.Low.ShouldBe(low);
|
|
|
|
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);
|
|
}
|
|
}
|