added sanity checks
.NET Test / .NET tests (push) Successful in 3m55s

This commit is contained in:
2026-09-18 14:10:24 +04:00
parent b5a8bd2580
commit 5797bf4884
44 changed files with 419 additions and 146 deletions
@@ -1,6 +1,3 @@
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class ArithmeticRangeTests
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleArithmeticTests
@@ -332,6 +328,53 @@ public class DoubleDoubleArithmeticTests
Check(6.0 / new DoubleDouble(2.0), 3.0, 0.0);
}
[Theory]
[InlineData(2.0)]
[InlineData(3.0)]
public void ScalarAdditionAndSubtractionRetainTheMiddleSumResidual(double magnitude)
{
// Exact Fraction arithmetic on the stored E components confirms that
// all signed E +/- 2 and E +/- 3 results fit exactly in two components.
// Compare exact dyadic sums, not another overload or a rounded tolerance.
foreach (double valueSign in new[] { -1.0, 1.0 })
{
foreach (double scalarSign in new[] { -1.0, 1.0 })
{
DoubleDouble value = valueSign * DoubleDouble.E;
double scalar = scalarSign * magnitude;
BigInteger expectedSum = Units(value) + Units(scalar);
BigInteger expectedDifference = Units(value) - Units(scalar);
Units(value + scalar).ShouldBe(expectedSum);
Units(scalar + value).ShouldBe(expectedSum);
Units(value - scalar).ShouldBe(expectedDifference);
Units(scalar - value).ShouldBe(-expectedDifference);
}
}
}
[Theory]
[InlineData("+")]
[InlineData("-")]
public void ScalarSumsAndDifferencesMatchExpansionBitsAcrossRanges(string operation)
{
// Bitwise compatibility; independent exact-rational accuracy checks are separate.
(double[] scalars, DoubleDouble[] values) = ScalarArithmeticCompatibilityCases();
foreach (DoubleDouble value in values)
{
foreach (double scalar in scalars)
{
DoubleDouble expanded = new(scalar);
DoubleDouble expected = operation == "+" ? value + expanded : value - expanded;
DoubleDouble actual = operation == "+" ? value + scalar : value - scalar;
CheckBoundary(actual, expected.High, expected.Low);
DoubleDouble expectedReverse = operation == "+" ? expanded + value : expanded - value;
DoubleDouble actualReverse = operation == "+" ? scalar + value : scalar - value;
CheckBoundary(actualReverse, expectedReverse.High, expectedReverse.Low);
}
}
}
[Fact]
public void ExpansionCancellationRetainsExactComponentsAcrossTheAdditionGuard()
{
@@ -579,6 +622,25 @@ public class DoubleDoubleArithmeticTests
}
}
[Fact]
public void DivisionOverloadsPreserveComponentBitsAcrossRangesAndSpecialValues()
{
// Compatibility, not an accuracy oracle. GeneralArithmeticMeetsExactRationalBound
// separately checks complete results against exact rational bounds.
(double[] scalars, DoubleDouble[] values) = ScalarArithmeticCompatibilityCases();
foreach (DoubleDouble value in values)
{
foreach (double scalar in scalars)
{
DoubleDouble expectedQuotient = value / new DoubleDouble(scalar);
CheckBoundary(value / scalar, expectedQuotient.High, expectedQuotient.Low);
DoubleDouble expectedReverseQuotient = new DoubleDouble(scalar) / value;
CheckBoundary(scalar / value, expectedReverseQuotient.High, expectedReverseQuotient.Low);
}
}
}
[Fact]
public void DivisionRetainsSubnormalCorrectionsWithOrdinaryHighComponents()
{
@@ -597,6 +659,38 @@ public class DoubleDoubleArithmeticTests
}
}
[Theory]
[InlineData(-1000)]
[InlineData(-1021)]
[InlineData(-1022)]
[InlineData(-1023)]
public void ScalarDivisionPreservesSparseCorrectionProductsNearUnderflow(int lowExponent)
{
// At -1000, the second product is normal but its exact residual is
// (962132647665515 / 1073741824) * epsilon, which rounds to 896056 * epsilon.
// Derived from exact binary64 rational products; the other rows bracket
// the correction product's normal/subnormal transition.
foreach (double numeratorSign in new[] { -1.0, 1.0 })
{
foreach (double denominatorSign in new[] { -1.0, 1.0 })
{
foreach (double lowSign in new[] { -1.0, 1.0 })
{
double low = Math.ScaleB(lowSign, lowExponent);
double denominator = denominatorSign * 1.1;
DoubleDouble numerator = DoubleDouble.FromComponents(numeratorSign * 1.1, low);
DoubleDouble actual = numerator / denominator;
DoubleDouble expanded = numerator / new DoubleDouble(denominator);
CheckBoundary(actual, expanded.High, expanded.Low);
// Exact quotient = +/-1 + low/denominator. Its high cannot
// change here; binary64 division independently rounds the low.
CheckBoundary(actual, numeratorSign / denominatorSign, low / denominator);
AssertRelative(actual, Units(numerator) << 1074, Units(denominator));
}
}
}
}
[Fact]
public void SubnormalProductsWithLargeNormalsRetainExactResultsInBothOrders()
{
@@ -884,6 +978,39 @@ public class DoubleDoubleArithmeticTests
}
}
private static (double[] Scalars, DoubleDouble[] Values) ScalarArithmeticCompatibilityCases()
{
List<double> scalars = [0.0, -0.0, 1.0, -1.0, 2.0, -2.0, 3.0, -3.0, 100.0, -100.0,
double.MaxValue, double.MinValue, double.PositiveInfinity,
double.NegativeInfinity, double.NaN];
List<DoubleDouble> values = [DoubleDouble.E, -DoubleDouble.E];
// Include addition/division dispatch edges, exponent extremes, dense/sparse
// lows of either sign, and significands on either side of a binade.
int[] exponents = [-1074, -1022, -451, -450, -1, 0, 1, 450, 451, 1020, 1021, 1023];
double[] significands = [1.0, Math.BitIncrement(1.0), 1.5, Math.BitDecrement(2.0)];
foreach (int exponent in exponents)
{
foreach (double significand in significands)
{
foreach (double sign in new[] { -1.0, 1.0 })
{
double high = Math.ScaleB(sign * significand, exponent);
scalars.Add(high);
double denseLow = Math.ScaleB(1.0, exponent - 54);
foreach (double low in new[] { 0.0, denseLow, -denseLow, double.Epsilon, -double.Epsilon })
{
values.Add(DoubleDouble.FromComponents(high, low));
}
}
}
}
foreach (double scalar in scalars)
{
values.Add(new DoubleDouble(scalar));
}
return (scalars.ToArray(), values.ToArray());
}
private static void AssertDivisionMatchesPrevious(DoubleDouble left, DoubleDouble right)
{
// Freeze the pre-specialization expression. The unchanged public operators
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleBoundaryTests
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleCbrtTests
@@ -1,6 +1,3 @@
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleComparisonTests
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleConversionTests
@@ -1,8 +1,5 @@
using System.Globalization;
using System.Numerics;
using Just.PreciseMath.Tests.ReferenceData;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,6 +1,4 @@
using System.Globalization;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleHypotTests
@@ -1,7 +1,4 @@
using System.Globalization;
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,7 +1,4 @@
using System.Globalization;
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,6 +1,3 @@
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleRepresentationTests
@@ -1,6 +1,3 @@
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleRootFunctionsTests
@@ -1,8 +1,5 @@
using System.Globalization;
using System.Numerics;
using Just.PreciseMath.Tests.ReferenceData;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleSignedNumberTests
@@ -1,6 +1,4 @@
using System.Globalization;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,7 +1,4 @@
using System.Globalization;
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleTests
@@ -1,7 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,8 +1,5 @@
using System.Globalization;
using System.Numerics;
using Just.PreciseMath.Tests.ReferenceData;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class PreciseMathInvSqrtTests
@@ -1,8 +1,5 @@
using System.Globalization;
using System.Numerics;
using Just.PreciseMath.Tests.ReferenceData;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class PreciseMathPowTests
@@ -1,6 +1,3 @@
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class PreciseMathRealPowSpecialTests
@@ -1,8 +1,5 @@
using System.Globalization;
using System.Numerics;
using Just.PreciseMath.Tests.ReferenceData;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class PreciseMathReciprocalTests
@@ -1,7 +1,3 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class PreciseMathSqrtTests
@@ -1,6 +1,3 @@
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class PreciseMathTests
@@ -1,5 +1,3 @@
using Xunit;
namespace Just.PreciseMath.Tests.ReferenceData;
// Generated by generate_exp.py; do not derive expected values from DD arithmetic.
@@ -9,8 +9,6 @@
// absolute; reference rounding uncertainty is bounded by 2^-350 relative.
// Tiny corrections to 1/-1 below 120 digits are not component-retention oracles.
// Signed zeros, infinities, NaNs, and enormous arguments are tested separately.
using Xunit;
namespace Just.PreciseMath.Tests.ReferenceData;
internal static class ExponentialFunctionsReferenceData
@@ -1,7 +1,5 @@
// Generated by generate_log.py; do not hand-edit reference literals.
// Exact binary64 sums; Decimal.ln at 450/650 digits, rounded to 120 digits.
using Xunit;
namespace Just.PreciseMath.Tests.ReferenceData;
internal static class LogReferenceData
@@ -1,8 +1,6 @@
// Generated by generate_real_pow.py; do not hand-edit reference literals.
// Exact binary64 sums; Decimal ln/exp at 450/650 digits, rounded to 120 digits.
// Reference uncertainty is explicitly allowed as 2^-350 relative in the tests.
using Xunit;
namespace Just.PreciseMath.Tests.ReferenceData;
internal static class RealPowReferenceData
@@ -1,7 +1,5 @@
// Generated by generate_rootn.py; do not hand-edit reference literals.
// Exact component sums; Decimal ln/exp at 450/650 digits, rounded to 120 digits.
using Xunit;
namespace Just.PreciseMath.Tests.ReferenceData;
internal static class RootNReferenceData
@@ -0,0 +1,27 @@
namespace Just.PreciseMath.Tests.SanityChecks;
public class DoubleDoubleAdditionChecks
{
public static IEnumerable<TheoryDataRow<DoubleDouble, DoubleDouble, DoubleDouble>> ReferenceCases =>
[
(new DoubleDouble(1.0), new DoubleDouble(1.0), new DoubleDouble(2.0)),
(new DoubleDouble(1.0), new DoubleDouble(2.0), new DoubleDouble(3.0)),
(new DoubleDouble(1.25), new DoubleDouble(2.5), new DoubleDouble(3.75)),
(new DoubleDouble(65535.0), new DoubleDouble(1.0), new DoubleDouble(65536.0)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), DoubleDouble.Parse("-812563124576179134504", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(42.0)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(127.0), DoubleDouble.Parse("812563124576179134673", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(27.0), DoubleDouble.Parse("812563124576179134573", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(2.70), DoubleDouble.Parse("812563124576179134548.7", System.Globalization.CultureInfo.InvariantCulture)),
];
[Theory]
[MemberData(nameof(ReferenceCases))]
public void AdditionShouldProduceExpectedResults(DoubleDouble left, DoubleDouble right, DoubleDouble expected)
{
DoubleDouble result = left + right;
result.High.ShouldBe(expected.High);
result.Low.ShouldBe(expected.Low);
result.ShouldBe(expected);
}
}
@@ -0,0 +1,108 @@
namespace Just.PreciseMath.Tests.SanityChecks;
public class DoubleDoubleArithmeticEqualityChecks
{
public static IEnumerable<TheoryDataRow<double>> ReferenceCases => [1.0, 2.0, 3.0, 100.0, 0.1, 0.5, 0.333333333333333333, -1.0, 1553.0, 1.0e+10];
[Theory]
[MemberData(nameof(ReferenceCases))]
public void AdditionShouldProduceIdenticalResults(double reference)
{
DoubleDouble testValue = DoubleDouble.E; // any value with full precision range used
DoubleDouble ddxdd_Result = testValue + (new DoubleDouble(reference));
DoubleDouble ddxd_Result = testValue + reference;
DoubleDouble dxdd_Result = reference + testValue;
ddxdd_Result.High.ShouldBe(ddxd_Result.High);
ddxdd_Result.Low.ShouldBe(ddxd_Result.Low);
ddxdd_Result.ShouldBe(ddxd_Result);
ddxdd_Result.High.ShouldBe(dxdd_Result.High);
ddxdd_Result.Low.ShouldBe(dxdd_Result.Low);
ddxdd_Result.ShouldBe(dxdd_Result);
ddxd_Result.High.ShouldBe(dxdd_Result.High);
ddxd_Result.Low.ShouldBe(dxdd_Result.Low);
ddxd_Result.ShouldBe(dxdd_Result);
}
[Theory]
[MemberData(nameof(ReferenceCases))]
public void SubtractionFromShouldProduceIdenticalResults(double reference)
{
DoubleDouble testValue = DoubleDouble.E; // any value with full precision range used
DoubleDouble ddxdd_Result = testValue - (new DoubleDouble(reference));
DoubleDouble ddxd_Result = testValue - reference;
ddxdd_Result.High.ShouldBe(ddxd_Result.High);
ddxdd_Result.Low.ShouldBe(ddxd_Result.Low);
ddxdd_Result.ShouldBe(ddxd_Result);
}
[Theory]
[MemberData(nameof(ReferenceCases))]
public void SubtractionShouldProduceIdenticalResults(double reference)
{
DoubleDouble testValue = DoubleDouble.E; // any value with full precision range used
DoubleDouble ddxdd_Result = (new DoubleDouble(reference)) - testValue;
DoubleDouble dxdd_Result = reference - testValue;
ddxdd_Result.High.ShouldBe(dxdd_Result.High);
ddxdd_Result.Low.ShouldBe(dxdd_Result.Low);
ddxdd_Result.ShouldBe(dxdd_Result);
}
[Theory]
[MemberData(nameof(ReferenceCases))]
public void MultiplicationShouldProduceIdenticalResults(double reference)
{
DoubleDouble testValue = DoubleDouble.E; // any value with full precision range used
DoubleDouble ddxdd_Result = testValue * (new DoubleDouble(reference));
DoubleDouble ddxd_Result = testValue * reference;
DoubleDouble dxdd_Result = reference * testValue;
ddxdd_Result.High.ShouldBe(ddxd_Result.High);
ddxdd_Result.Low.ShouldBe(ddxd_Result.Low);
ddxdd_Result.ShouldBe(ddxd_Result);
ddxdd_Result.High.ShouldBe(dxdd_Result.High);
ddxdd_Result.Low.ShouldBe(dxdd_Result.Low);
ddxdd_Result.ShouldBe(dxdd_Result);
ddxd_Result.High.ShouldBe(dxdd_Result.High);
ddxd_Result.Low.ShouldBe(dxdd_Result.Low);
ddxd_Result.ShouldBe(dxdd_Result);
}
[Theory]
[MemberData(nameof(ReferenceCases))]
public void DivisionShouldProduceIdenticalResults(double reference)
{
DoubleDouble testValue = DoubleDouble.E; // any value with full precision range used
DoubleDouble ddxdd_Result = testValue / (new DoubleDouble(reference));
DoubleDouble ddxd_Result = testValue / reference;
ddxdd_Result.High.ShouldBe(ddxd_Result.High);
ddxdd_Result.Low.ShouldBe(ddxd_Result.Low);
ddxdd_Result.ShouldBe(ddxd_Result);
}
[Theory]
[MemberData(nameof(ReferenceCases))]
public void DivisionByShouldProduceIdenticalResults(double reference)
{
DoubleDouble testValue = DoubleDouble.E; // any value with full precision range used
DoubleDouble ddxdd_Result = (new DoubleDouble(reference)) / testValue;
DoubleDouble dxdd_Result = reference / testValue;
ddxdd_Result.High.ShouldBe(dxdd_Result.High);
ddxdd_Result.Low.ShouldBe(dxdd_Result.Low);
ddxdd_Result.ShouldBe(dxdd_Result);
}
}
@@ -0,0 +1,29 @@
namespace Just.PreciseMath.Tests.SanityChecks;
public class DoubleDoubleDivisionChecks
{
public static IEnumerable<TheoryDataRow<DoubleDouble, DoubleDouble, DoubleDouble>> ReferenceCases =>
[
(new DoubleDouble(6.0), new DoubleDouble(2.0), new DoubleDouble(3.0)),
(new DoubleDouble(6.0), new DoubleDouble(3.0), new DoubleDouble(2.0)),
(new DoubleDouble(5.0), new DoubleDouble(2.0), new DoubleDouble(2.5)),
(new DoubleDouble(1.0), new DoubleDouble(4.0), new DoubleDouble(0.25)),
(new DoubleDouble(3.75), new DoubleDouble(1.25), new DoubleDouble(3.0)),
(new DoubleDouble(65536.0), new DoubleDouble(1024.0), new DoubleDouble(64.0)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(2.0), DoubleDouble.Parse("406281562288089567273", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(4.0), DoubleDouble.Parse("203140781144044783636.5", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(8.0), DoubleDouble.Parse("101570390572022391818.25", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(1.0)),
];
[Theory]
[MemberData(nameof(ReferenceCases))]
public void DivisionShouldProduceExpectedResults(DoubleDouble left, DoubleDouble right, DoubleDouble expected)
{
DoubleDouble result = left / right;
result.High.ShouldBe(expected.High);
result.Low.ShouldBe(expected.Low);
result.ShouldBe(expected);
}
}
@@ -0,0 +1,29 @@
namespace Just.PreciseMath.Tests.SanityChecks;
public class DoubleDoubleMultiplicationChecks
{
public static IEnumerable<TheoryDataRow<DoubleDouble, DoubleDouble, DoubleDouble>> ReferenceCases =>
[
(new DoubleDouble(1.0), new DoubleDouble(1.0), new DoubleDouble(1.0)),
(new DoubleDouble(2.0), new DoubleDouble(3.0), new DoubleDouble(6.0)),
(new DoubleDouble(1.25), new DoubleDouble(2.5), new DoubleDouble(3.125)),
(new DoubleDouble(-1.25), new DoubleDouble(2.5), new DoubleDouble(-3.125)),
(new DoubleDouble(65535.0), new DoubleDouble(65536.0), new DoubleDouble(4294901760.0)),
(new DoubleDouble(65536.0), new DoubleDouble(65536.0), new DoubleDouble(4294967296.0)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(2.0), DoubleDouble.Parse("1625126249152358269092", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(0.5), DoubleDouble.Parse("406281562288089567273", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(27.0), DoubleDouble.Parse("21939204363556836632742", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(127.0), DoubleDouble.Parse("103195516821174750087342", System.Globalization.CultureInfo.InvariantCulture)),
];
[Theory]
[MemberData(nameof(ReferenceCases))]
public void MultiplicationShouldProduceExpectedResults(DoubleDouble left, DoubleDouble right, DoubleDouble expected)
{
DoubleDouble result = left * right;
result.High.ShouldBe(expected.High);
result.Low.ShouldBe(expected.Low);
result.ShouldBe(expected);
}
}
@@ -0,0 +1,30 @@
namespace Just.PreciseMath.Tests.SanityChecks;
public class DoubleDoubleSubtractionChecks
{
public static IEnumerable<TheoryDataRow<DoubleDouble, DoubleDouble, DoubleDouble>> ReferenceCases =>
[
(new DoubleDouble(2.0), new DoubleDouble(1.0), new DoubleDouble(1.0)),
(new DoubleDouble(1.0), new DoubleDouble(2.0), new DoubleDouble(-1.0)),
(new DoubleDouble(3.75), new DoubleDouble(2.5), new DoubleDouble(1.25)),
(new DoubleDouble(2.5), new DoubleDouble(3.75), new DoubleDouble(-1.25)),
(new DoubleDouble(65536.0), new DoubleDouble(1.0), new DoubleDouble(65535.0)),
(new DoubleDouble(65536.0), new DoubleDouble(65535.0), new DoubleDouble(1.0)),
(DoubleDouble.Parse("812563124576179134673", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(127.0), DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(127.0), DoubleDouble.Parse("812563124576179134419", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), DoubleDouble.Parse("812563124576179134504", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(42.0)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(0.5), DoubleDouble.Parse("812563124576179134545.5", System.Globalization.CultureInfo.InvariantCulture)),
(DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), DoubleDouble.Parse("812563124576179134546", System.Globalization.CultureInfo.InvariantCulture), new DoubleDouble(0.0)),
];
[Theory]
[MemberData(nameof(ReferenceCases))]
public void SubtractionShouldProduceExpectedResults(DoubleDouble left, DoubleDouble right, DoubleDouble expected)
{
DoubleDouble result = left - right;
result.High.ShouldBe(expected.High);
result.Low.ShouldBe(expected.Low);
result.ShouldBe(expected);
}
}