This commit is contained in:
@@ -116,6 +116,8 @@ public class PreciseMathSqrtTests
|
||||
new(Math.BitIncrement(Math.ScaleB(1.0, -1022))), new(double.MaxValue),
|
||||
DoubleDouble.FromComponents(double.MaxValue, Math.ScaleB(1.0, 969)),
|
||||
DoubleDouble.FromComponents(double.MaxValue, -Math.ScaleB(1.0, 969)),
|
||||
DoubleDouble.FromComponents(double.MaxValue, Math.BitDecrement(Math.ScaleB(1.0, 970))),
|
||||
DoubleDouble.FromComponents(double.MaxValue, -Math.ScaleB(1.0, 970)),
|
||||
DoubleDouble.FromComponents(1.0, Math.ScaleB(1.0, -53)),
|
||||
DoubleDouble.FromComponents(1.0, -Math.ScaleB(1.0, -54)),
|
||||
DoubleDouble.FromComponents(4.0, Math.ScaleB(1.0, -51)),
|
||||
@@ -126,6 +128,84 @@ public class PreciseMathSqrtTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RootRoundingMidpointsAndAdjacentLowsMeetTheBound()
|
||||
{
|
||||
// Exact squares of root midpoints 1 + 2^-53 and 2 - 2^-53:
|
||||
// (1 + 2^-52) + 2^-106 and (4 - 2^-51) + 2^-106.
|
||||
// Bracket each with adjacent low doubles, then exercise exponent parity
|
||||
// and rescaling. At tiny exponents the input itself loses low precision;
|
||||
// the oracle always checks the exact stored input, not the unscaled square.
|
||||
double midpointLow = Math.ScaleB(1.0, -106);
|
||||
for (int exponent = -1074; exponent <= 1022; ++exponent)
|
||||
{
|
||||
foreach (double high in new[] { Math.BitIncrement(1.0), Math.BitDecrement(4.0) })
|
||||
{
|
||||
foreach (double low in new[] { Math.BitDecrement(midpointLow), midpointLow, Math.BitIncrement(midpointLow) })
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(Math.ScaleB(high, exponent), Math.ScaleB(low, exponent));
|
||||
if (DoubleDouble.IsFinite(input) && input.High > 0.0)
|
||||
{
|
||||
AssertSqrtBound(input, DDMath.Sqrt(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PublicFactoryNormalizesLegacyZeroHighInputsBeforeTakingTheRoot()
|
||||
{
|
||||
// Legacy raw (0, low) examples are normalized at the public boundary.
|
||||
foreach (double low in new[] { double.Epsilon, 1e-308, 2.0, double.MaxValue })
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(0.0, low);
|
||||
AssertSqrtBound(input, DDMath.Sqrt(input));
|
||||
}
|
||||
DoubleDouble.IsNaN(DDMath.Sqrt(DoubleDouble.FromComponents(0.0, -1.0))).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DenseLowNormalizationBoundariesMeetTheBound()
|
||||
{
|
||||
// Half-ulp lows and their neighbors test both sides of input normalization.
|
||||
Random random = new(161803);
|
||||
for (int exponent = -1074; exponent <= 1023; ++exponent)
|
||||
{
|
||||
double high = Math.ScaleB(1.0 + random.NextDouble(), exponent);
|
||||
double halfUlp = Math.ScaleB(1.0, exponent - 53);
|
||||
foreach (double magnitude in new[] { Math.BitDecrement(halfUlp), halfUlp, Math.BitIncrement(halfUlp) })
|
||||
{
|
||||
foreach (double low in new[] { magnitude, -magnitude })
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(high, low);
|
||||
if (DoubleDouble.IsFinite(input) && input.High > 0.0)
|
||||
{
|
||||
AssertSqrtBound(input, DDMath.Sqrt(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RepresentableSparseCorrectionsAreNotDiscarded()
|
||||
{
|
||||
// sqrt(1+d) = 1+d/2+O(d^2). For these dyadic d, the quadratic term
|
||||
// is below half an ulp of d/2, including when d/2 is the minimum subnormal.
|
||||
foreach (int exponent in new[] { -100, -500, -1000, -1073 })
|
||||
{
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
double low = Math.ScaleB(sign, exponent);
|
||||
DoubleDouble actual = DDMath.Sqrt(DoubleDouble.FromComponents(1.0, low));
|
||||
actual.High.ShouldBe(1.0);
|
||||
actual.Low.ShouldBe(Math.ScaleB(sign, exponent - 1));
|
||||
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertSqrtBound(DoubleDouble input, DoubleDouble actual)
|
||||
{
|
||||
DoubleDouble.IsFinite(actual).ShouldBeTrue();
|
||||
|
||||
Reference in New Issue
Block a user