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)] 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))). DoubleDouble value = name switch { "PI" => DoubleDouble.PI, "E" => DoubleDouble.E, "LN2" => DoubleDouble.LN2, _ => throw new ArgumentOutOfRangeException(nameof(name)), }; value.High.ShouldBe(high); value.Low.ShouldBe(low); } [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 AdditiveIdentity(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 MultiplicativeIdentity(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); } }