Files
just 5797bf4884
.NET Test / .NET tests (push) Successful in 3m55s
added sanity checks
2026-09-18 14:10:24 +04:00

105 lines
4.6 KiB
C#

namespace Just.PreciseMath.Tests;
public class DoubleDoubleComparisonTests
{
[Fact]
public void RelationalOperatorsCoverEqualHighComponentsAndSpecialValues()
{
// Explicit numerical order, including opposite low-component signs and
// equivalent signed zeros. NaNs are tested separately as unordered.
DoubleDouble[] ordered =
[
new(double.NegativeInfinity), new(-double.MaxValue),
DoubleDouble.FromComponents(-1.0, -double.Epsilon), new(-1.0), DoubleDouble.FromComponents(-1.0, double.Epsilon),
new(-double.Epsilon), new(-0.0), new(0.0), new(double.Epsilon),
DoubleDouble.FromComponents(1.0, -double.Epsilon), new(1.0), DoubleDouble.FromComponents(1.0, double.Epsilon),
new(double.MaxValue), new(double.PositiveInfinity)
];
for (int i = 0; i < ordered.Length; ++i)
{
for (int j = 0; j < ordered.Length; ++j)
{
bool bothZero = (i is 6 or 7) && (j is 6 or 7);
(ordered[i] < ordered[j]).ShouldBe(i < j && !bothZero);
(ordered[i] > ordered[j]).ShouldBe(i > j && !bothZero);
(ordered[i] <= ordered[j]).ShouldBe(i <= j || bothZero);
(ordered[i] >= ordered[j]).ShouldBe(i >= j || bothZero);
}
}
}
[Fact]
public void OrderingUsesLowComponentAndSupportsCollections()
{
DoubleDouble one = DoubleDouble.One;
DoubleDouble above = DoubleDouble.FromComponents(1.0, Math.ScaleB(1.0, -100));
(above > one).ShouldBeTrue();
(one < above).ShouldBeTrue();
(one <= above).ShouldBeTrue();
(above >= one).ShouldBeTrue();
above.CompareTo(one).ShouldBeGreaterThan(0);
new SortedSet<DoubleDouble> { above, one }.Count.ShouldBe(2);
((IComparable)one).CompareTo(null).ShouldBe(1);
Should.Throw<ArgumentException>(() => ((IComparable)one).CompareTo("1"));
}
[Fact]
public void DistinctLowComponentsAreNotCollapsedByOrderingOrCollections()
{
// Review-1 §3: a = (1, 0) and b = (1, 1e-30) are distinct values. The former
// high-only CompareTo reported them equal, so a SortedSet retained only one.
DoubleDouble a = DoubleDouble.One;
DoubleDouble b = DoubleDouble.FromComponents(1.0, 1e-30);
b.High.ShouldBe(1.0);
b.Low.ShouldBe(1e-30);
(b > a).ShouldBeTrue();
(a < b).ShouldBeTrue();
b.CompareTo(a).ShouldBeGreaterThan(0);
b.Equals(a).ShouldBeFalse();
new SortedSet<DoubleDouble> { b, a }.Count.ShouldBe(2);
}
[Fact]
public void NaNOperatorsAreUnorderedButCompareToProvidesTotalOrder()
{
DoubleDouble nan = DoubleDouble.NaN;
foreach (DoubleDouble other in new[] { nan, DoubleDouble.Zero, new DoubleDouble(-0.0),
new DoubleDouble(double.NegativeInfinity), new DoubleDouble(double.PositiveInfinity),
DoubleDouble.FromComponents(1.0, double.Epsilon), DoubleDouble.FromComponents(-1.0, -double.Epsilon) })
{
(nan < other).ShouldBeFalse();
(nan > other).ShouldBeFalse();
(nan <= other).ShouldBeFalse();
(nan >= other).ShouldBeFalse();
(other < nan).ShouldBeFalse();
(other > nan).ShouldBeFalse();
(other <= nan).ShouldBeFalse();
(other >= nan).ShouldBeFalse();
}
nan.CompareTo(nan).ShouldBe(0);
nan.CompareTo(DoubleDouble.Zero).ShouldBeLessThan(0);
}
[Theory]
[InlineData(1.0, 0.0)]
[InlineData(0.0, -1.0)]
[InlineData(double.NaN, 0.0)]
[InlineData(0.0, double.NaN)]
[InlineData(double.PositiveInfinity, 0.0)]
[InlineData(0.0, double.NegativeInfinity)]
[InlineData(double.PositiveInfinity, double.NegativeInfinity)]
public void ClassificationFollowsCanonicalHigh(double high, double low)
{
DoubleDouble value = DoubleDouble.FromComponents(high, low);
DoubleDouble.IsNaN(value).ShouldBe(double.IsNaN(value.High));
DoubleDouble.IsFinite(value).ShouldBe(double.IsFinite(value.High));
DoubleDouble.IsInfinity(value).ShouldBe(double.IsInfinity(value.High));
DoubleDouble.IsPositiveInfinity(value).ShouldBe(double.IsPositiveInfinity(value.High));
DoubleDouble.IsNegativeInfinity(value).ShouldBe(double.IsNegativeInfinity(value.High));
DoubleDouble.IsNegative(value).ShouldBe(double.IsNegative(value.High));
value.Decompose(out double actualHigh, out double actualLow);
actualHigh.ShouldBe(value.High);
actualLow.ShouldBe(value.Low);
}
}