This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user