This commit is contained in:
@@ -2,6 +2,55 @@ namespace Just.PreciseMath.Tests;
|
||||
|
||||
public class DoubleDoubleArithmeticTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("+")]
|
||||
[InlineData("-")]
|
||||
[InlineData("*")]
|
||||
[InlineData("/")]
|
||||
public void NamedArithmeticMatchesOperatorComponentBits(string operation)
|
||||
{
|
||||
// Compatibility, not an accuracy oracle. The existing rational tests
|
||||
// independently validate the kernels reached through these operators.
|
||||
DoubleDouble[] values = MultiplicationCommutativityCases();
|
||||
foreach (DoubleDouble left in values)
|
||||
{
|
||||
foreach (DoubleDouble right in values)
|
||||
{
|
||||
(DoubleDouble actual, DoubleDouble expected)[] pairs = operation switch
|
||||
{
|
||||
"+" => [(DoubleDouble.Add(left, right), left + right),
|
||||
(DoubleDouble.Add(left, right.High), left + right.High),
|
||||
(DoubleDouble.Add(left.High, right), left.High + right)],
|
||||
"-" => [(DoubleDouble.Subtract(left, right), left - right),
|
||||
(DoubleDouble.Subtract(left, right.High), left - right.High),
|
||||
(DoubleDouble.Subtract(left.High, right), left.High - right)],
|
||||
"*" => [(DoubleDouble.Multiply(left, right), left * right),
|
||||
(DoubleDouble.Multiply(left, right.High), left * right.High),
|
||||
(DoubleDouble.Multiply(left.High, right), left.High * right)],
|
||||
"/" => [(DoubleDouble.Divide(left, right), left / right),
|
||||
(DoubleDouble.Divide(left, right.High), left / right.High),
|
||||
(DoubleDouble.Divide(left.High, right), left.High / right)],
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(operation))
|
||||
};
|
||||
foreach ((DoubleDouble actual, DoubleDouble expected) in pairs)
|
||||
{
|
||||
CheckBoundary(actual, expected.High, expected.Low);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NamedUnaryArithmeticMatchesOperatorComponentBits()
|
||||
{
|
||||
foreach (DoubleDouble value in MultiplicationCommutativityCases())
|
||||
{
|
||||
CheckBoundary(DoubleDouble.Plus(value), value.High, value.Low);
|
||||
DoubleDouble negated = -value;
|
||||
CheckBoundary(DoubleDouble.Negate(value), negated.High, negated.Low);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(6.0, 2.0, 8.0, 4.0, 12.0, 3.0)]
|
||||
[InlineData(-6.0, 2.0, -4.0, -8.0, -12.0, -3.0)]
|
||||
@@ -1050,7 +1099,7 @@ public class DoubleDoubleArithmeticTests
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
CheckBoundary(DoubleDouble.Square(sign * below), Math.BitDecrement(highSquared), Math.ScaleB(1.0, (2 * exponent) - 108));
|
||||
CheckBoundary(DDMath.Square(sign * above), Math.BitIncrement(highSquared), Math.ScaleB(1.0, (2 * exponent) - 106));
|
||||
CheckBoundary(DoubleDouble.Square(sign * above), Math.BitIncrement(highSquared), Math.ScaleB(1.0, (2 * exponent) - 106));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1060,7 +1109,6 @@ public class DoubleDoubleArithmeticTests
|
||||
foreach (double value in new[] { 0.0, -0.0, double.NaN, double.PositiveInfinity, double.NegativeInfinity })
|
||||
{
|
||||
CheckBits(DoubleDouble.Square(new DoubleDouble(value)), value * value);
|
||||
CheckBits(DDMath.Square(new DoubleDouble(value)), value * value);
|
||||
}
|
||||
for (int exponent = -1074; exponent <= 1023; exponent++)
|
||||
{
|
||||
@@ -1139,7 +1187,6 @@ public class DoubleDoubleArithmeticTests
|
||||
DoubleDouble.IsZero(actual).ShouldBe((numerator << 1) <= denominator);
|
||||
}
|
||||
CheckBoundary(DoubleDouble.Square(-value), actual.High, actual.Low);
|
||||
CheckBoundary(DDMath.Square(value), actual.High, actual.Low);
|
||||
}
|
||||
|
||||
private static DoubleDouble[] MultiplicationCommutativityCases()
|
||||
|
||||
@@ -36,7 +36,6 @@ public class DoubleDoubleCbrtTests
|
||||
{
|
||||
AssertBound(input, DoubleDouble.Cbrt(input));
|
||||
AssertBound(-input, DoubleDouble.Cbrt(-input));
|
||||
AssertBits(DoubleDouble.Cbrt(input), DDMath.Cbrt(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ public class DoubleDoubleExponentialFunctionsTests
|
||||
DoubleDouble actual = Evaluate(operation, input);
|
||||
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
|
||||
AssertBits(actual, EvaluateGeneric(operation, input));
|
||||
AssertBits(actual, EvaluateFacade(operation, input));
|
||||
if (overflow)
|
||||
{
|
||||
AssertBits(DoubleDouble.PositiveInfinity, actual);
|
||||
@@ -57,7 +56,7 @@ public class DoubleDoubleExponentialFunctionsTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NaturalExponentialIsAvailableOnTheTypeAndMatchesFacadeBits()
|
||||
public void NaturalExponentialMatchesGenericDispatchBits()
|
||||
{
|
||||
DoubleDouble[] values = [DoubleDouble.Zero, DoubleDouble.NegativeZero,
|
||||
DoubleDouble.NaN, DoubleDouble.PositiveInfinity, DoubleDouble.NegativeInfinity,
|
||||
@@ -65,7 +64,7 @@ public class DoubleDoubleExponentialFunctionsTests
|
||||
new(double.MaxValue), DoubleDouble.FromComponents(1.0, Math.ScaleB(1.0, -1000))];
|
||||
foreach (DoubleDouble value in values)
|
||||
{
|
||||
AssertBits(DDMath.Exp(value), DoubleDouble.Exp(value));
|
||||
AssertBits(DoubleDouble.Exp(value), EvaluateGeneric("Exp", value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +116,6 @@ public class DoubleDoubleExponentialFunctionsTests
|
||||
DoubleDouble input = DoubleDouble.FromComponents(exponent, sign * double.Epsilon);
|
||||
DoubleDouble expected = DoubleDouble.FromComponents(Math.ScaleB(1.0, exponent), sign * expectedLow);
|
||||
AssertBits(expected, DoubleDouble.Exp2(input));
|
||||
AssertBits(expected, DDMath.Exp2(input));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +126,7 @@ public class DoubleDoubleExponentialFunctionsTests
|
||||
[InlineData("ExpM1")]
|
||||
[InlineData("Exp2M1")]
|
||||
[InlineData("Exp10M1")]
|
||||
public void CompleteContractHasGenericDispatchAndThinFacade(string operation)
|
||||
public void CompleteContractHasGenericDispatch(string operation)
|
||||
{
|
||||
bool minusOne = operation.EndsWith("M1", StringComparison.Ordinal);
|
||||
DoubleDouble[] values = [DoubleDouble.Zero, DoubleDouble.NegativeZero,
|
||||
@@ -141,7 +139,6 @@ public class DoubleDoubleExponentialFunctionsTests
|
||||
DoubleDouble actual = Evaluate(operation, value);
|
||||
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
|
||||
AssertBits(actual, EvaluateGeneric(operation, value));
|
||||
AssertBits(actual, EvaluateFacade(operation, value));
|
||||
if (DoubleDouble.IsZero(value))
|
||||
{
|
||||
AssertBits(minusOne ? value : DoubleDouble.One, actual);
|
||||
@@ -189,20 +186,6 @@ public class DoubleDoubleExponentialFunctionsTests
|
||||
};
|
||||
}
|
||||
|
||||
private static DoubleDouble EvaluateFacade(string operation, DoubleDouble value)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
"Exp" => DDMath.Exp(value),
|
||||
"Exp2" => DDMath.Exp2(value),
|
||||
"Exp10" => DDMath.Exp10(value),
|
||||
"ExpM1" => DDMath.ExpM1(value),
|
||||
"Exp2M1" => DDMath.Exp2M1(value),
|
||||
"Exp10M1" => DDMath.Exp10M1(value),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(operation))
|
||||
};
|
||||
}
|
||||
|
||||
private static BigInteger Units(double value)
|
||||
{
|
||||
long bits = BitConverter.DoubleToInt64Bits(value);
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
namespace Just.PreciseMath.Tests;
|
||||
|
||||
public class DoubleDoubleExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("Abs")]
|
||||
[InlineData("Square")]
|
||||
[InlineData("Reciprocal")]
|
||||
[InlineData("Sqrt")]
|
||||
[InlineData("InvSqrt")]
|
||||
[InlineData("Cbrt")]
|
||||
[InlineData("Exp")]
|
||||
[InlineData("Exp2")]
|
||||
[InlineData("Exp10")]
|
||||
[InlineData("ExpM1")]
|
||||
[InlineData("Exp2M1")]
|
||||
[InlineData("Exp10M1")]
|
||||
[InlineData("Log")]
|
||||
public void UnaryExtensionsPreserveStaticFunctionComponentBits(string operation)
|
||||
{
|
||||
// These are forwarding contracts, not accuracy oracles. The static
|
||||
// kernels have separate independent numerical-reference tests.
|
||||
foreach (DoubleDouble value in Values())
|
||||
{
|
||||
(DoubleDouble expected, DoubleDouble actual) = operation switch
|
||||
{
|
||||
"Abs" => (DoubleDouble.Abs(value), value.Abs()),
|
||||
"Square" => (DoubleDouble.Square(value), value.Square()),
|
||||
"Reciprocal" => (DoubleDouble.Reciprocal(value), value.Reciprocal()),
|
||||
"Sqrt" => (DoubleDouble.Sqrt(value), value.Sqrt()),
|
||||
"InvSqrt" => (DoubleDouble.InvSqrt(value), value.InvSqrt()),
|
||||
"Cbrt" => (DoubleDouble.Cbrt(value), value.Cbrt()),
|
||||
"Exp" => (DoubleDouble.Exp(value), value.Exp()),
|
||||
"Exp2" => (DoubleDouble.Exp2(value), value.Exp2()),
|
||||
"Exp10" => (DoubleDouble.Exp10(value), value.Exp10()),
|
||||
"ExpM1" => (DoubleDouble.ExpM1(value), value.ExpM1()),
|
||||
"Exp2M1" => (DoubleDouble.Exp2M1(value), value.Exp2M1()),
|
||||
"Exp10M1" => (DoubleDouble.Exp10M1(value), value.Exp10M1()),
|
||||
"Log" => (DoubleDouble.Log(value), value.Log()),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(operation))
|
||||
};
|
||||
AssertBits(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtensionsCanBeChainedWithoutChangingTheReceiver()
|
||||
{
|
||||
DoubleDouble value = new(-4.0);
|
||||
DoubleDouble actual = value.Abs().Square().Sqrt().Reciprocal();
|
||||
// All intermediate results are exactly representable binary powers.
|
||||
AssertBits(new DoubleDouble(0.25), actual);
|
||||
AssertBits(new DoubleDouble(-4.0), value);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(int.MinValue)]
|
||||
[InlineData(-7)]
|
||||
[InlineData(-2)]
|
||||
[InlineData(-1)]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
[InlineData(7)]
|
||||
[InlineData(int.MaxValue)]
|
||||
public void IntegerPowerExtensionPreservesStaticFunctionComponentBits(int exponent)
|
||||
{
|
||||
foreach (DoubleDouble value in Values())
|
||||
{
|
||||
AssertBits(DoubleDouble.Pow(value, exponent), value.Pow(exponent));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScalarPowerExtensionPreservesStaticFunctionComponentBits()
|
||||
{
|
||||
double[] exponents = [0.0, -0.0, 1.0, -1.0, 2.0, -2.0, 0.5, -0.5,
|
||||
1.25, -1.25, double.Epsilon, double.MaxValue, double.MinValue,
|
||||
double.NaN, double.PositiveInfinity, double.NegativeInfinity];
|
||||
foreach (DoubleDouble value in Values())
|
||||
{
|
||||
foreach (double exponent in exponents)
|
||||
{
|
||||
AssertBits(DoubleDouble.Pow(value, exponent), value.Pow(exponent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpansionPowerExtensionPreservesBothExponentComponents()
|
||||
{
|
||||
DoubleDouble[] exponents = [DoubleDouble.Zero, DoubleDouble.NegativeZero,
|
||||
DoubleDouble.One, DoubleDouble.NegativeOne, DoubleDouble.NaN,
|
||||
DoubleDouble.PositiveInfinity, DoubleDouble.NegativeInfinity,
|
||||
DoubleDouble.FromComponents(0.5, Math.ScaleB(1.0, -54)),
|
||||
DoubleDouble.FromComponents(0.5, -Math.ScaleB(1.0, -55)),
|
||||
DoubleDouble.FromComponents(1.0, double.Epsilon),
|
||||
DoubleDouble.FromComponents(Math.ScaleB(1.0, 54), 1.0)];
|
||||
foreach (DoubleDouble value in Values())
|
||||
{
|
||||
foreach (DoubleDouble exponent in exponents)
|
||||
{
|
||||
AssertBits(DoubleDouble.Pow(value, exponent), value.Pow(exponent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(int.MinValue)]
|
||||
[InlineData(-7)]
|
||||
[InlineData(-3)]
|
||||
[InlineData(-2)]
|
||||
[InlineData(-1)]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
[InlineData(3)]
|
||||
[InlineData(7)]
|
||||
[InlineData(int.MaxValue)]
|
||||
public void RootExtensionPreservesStaticFunctionComponentBits(int degree)
|
||||
{
|
||||
foreach (DoubleDouble value in Values())
|
||||
{
|
||||
AssertBits(DoubleDouble.RootN(value, degree), value.RootN(degree));
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<DoubleDouble> Values()
|
||||
{
|
||||
yield return DoubleDouble.Zero;
|
||||
yield return DoubleDouble.NegativeZero;
|
||||
yield return DoubleDouble.NaN;
|
||||
yield return DoubleDouble.PositiveInfinity;
|
||||
yield return DoubleDouble.NegativeInfinity;
|
||||
yield return new DoubleDouble(double.MaxValue);
|
||||
yield return new DoubleDouble(double.MinValue);
|
||||
yield return DoubleDouble.One;
|
||||
yield return DoubleDouble.NegativeOne;
|
||||
yield return DoubleDouble.Pi;
|
||||
yield return -DoubleDouble.Pi;
|
||||
// Square's dedicated kernel can differ from multiplication; near-one
|
||||
// and sparse pairs also exercise low-component retention in roots/log.
|
||||
yield return DoubleDouble.FromComponents(1.0, -Math.ScaleB(1.0, -54));
|
||||
yield return DoubleDouble.FromComponents(1.0, double.Epsilon);
|
||||
yield return DoubleDouble.FromComponents(1.0, -double.Epsilon);
|
||||
foreach (int exponent in new[] { -1074, -1022, -451, -450, -1, 0, 1, 450, 451, 1020, 1021, 1023 })
|
||||
{
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
double high = Math.ScaleB(sign * 1.25, exponent);
|
||||
double denseLow = Math.ScaleB(1.0, exponent - 54);
|
||||
foreach (double low in new[] { 0.0, denseLow, -denseLow, double.Epsilon, -double.Epsilon })
|
||||
{
|
||||
yield return DoubleDouble.FromComponents(high, low);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertBits(DoubleDouble expected, DoubleDouble actual)
|
||||
{
|
||||
BitConverter.DoubleToInt64Bits(actual.High).ShouldBe(BitConverter.DoubleToInt64Bits(expected.High));
|
||||
BitConverter.DoubleToInt64Bits(actual.Low).ShouldBe(BitConverter.DoubleToInt64Bits(expected.Low));
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,6 @@ public class DoubleDoubleHypotTests
|
||||
DoubleDouble result = DoubleDouble.Hypot(x, y);
|
||||
AssertBound(x, y, result);
|
||||
AssertBits(result, DoubleDouble.Hypot(-y, x));
|
||||
AssertBits(result, DDMath.Hypot(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +133,6 @@ public class DoubleDoubleHypotTests
|
||||
{
|
||||
AssertBound(new DoubleDouble(x), new DoubleDouble(y), new DoubleDouble(z), result);
|
||||
}
|
||||
AssertBits(result, DDMath.Hypot(new DoubleDouble(x), new DoubleDouble(y), new DoubleDouble(z)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +184,6 @@ public class DoubleDoubleHypotTests
|
||||
AssertBound(x, y, z, result);
|
||||
AssertBits(result, DoubleDouble.Hypot(-z, x, -y));
|
||||
AssertBits(result, DoubleDouble.Hypot(y, -z, x));
|
||||
AssertBits(result, DDMath.Hypot(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,7 +268,6 @@ public class DoubleDoubleHypotTests
|
||||
AssertBits(expected, DoubleDouble.Hypot(b, c, a));
|
||||
AssertBits(expected, DoubleDouble.Hypot(c, a, b));
|
||||
AssertBits(expected, DoubleDouble.Hypot(c, b, a));
|
||||
AssertBits(expected, DDMath.Hypot(a, b, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,7 +290,6 @@ public class DoubleDoubleHypotTests
|
||||
AssertBound(first, second, result);
|
||||
}
|
||||
AssertBits(result, DoubleDouble.Hypot(second, first));
|
||||
AssertBits(result, DDMath.Hypot(first, second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Just.PreciseMath.Tests;
|
||||
public class DoubleDoubleRootFunctionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void ExistingRootsAreAvailableOnTheTypeAndMatchFacadeBits()
|
||||
public void RootsAreCanonicalAndSqrtMatchesGenericDispatchBits()
|
||||
{
|
||||
DoubleDouble[] values = [DoubleDouble.Zero, DoubleDouble.NegativeZero,
|
||||
DoubleDouble.NaN, DoubleDouble.PositiveInfinity, DoubleDouble.NegativeInfinity,
|
||||
@@ -11,11 +11,16 @@ public class DoubleDoubleRootFunctionsTests
|
||||
DoubleDouble.FromComponents(1.0, Math.ScaleB(1.0, -1000))];
|
||||
foreach (DoubleDouble value in values)
|
||||
{
|
||||
AssertBits(DDMath.Sqrt(value), DoubleDouble.Sqrt(value));
|
||||
AssertBits(DDMath.InvSqrt(value), DoubleDouble.InvSqrt(value));
|
||||
AssertBits(DoubleDouble.Sqrt(value), GenericSqrt(value));
|
||||
DoubleDouble.IsCanonical(DoubleDouble.InvSqrt(value)).ShouldBeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
private static T GenericSqrt<T>(T value) where T : IRootFunctions<T>
|
||||
{
|
||||
return T.Sqrt(value);
|
||||
}
|
||||
|
||||
private static void AssertBits(DoubleDouble expected, DoubleDouble actual)
|
||||
{
|
||||
BitConverter.DoubleToInt64Bits(actual.High).ShouldBe(BitConverter.DoubleToInt64Bits(expected.High));
|
||||
|
||||
@@ -68,7 +68,7 @@ public class DoubleDoubleRootNTests
|
||||
{
|
||||
AssertBits(value, DoubleDouble.RootN(value, 1));
|
||||
AssertBits(-value, DoubleDouble.RootN(-value, 1));
|
||||
AssertBits(DDMath.Reciprocal(value), DoubleDouble.RootN(value, -1));
|
||||
AssertBits(DoubleDouble.Reciprocal(value), DoubleDouble.RootN(value, -1));
|
||||
AssertBits(DoubleDouble.Sqrt(value), DoubleDouble.RootN(value, 2));
|
||||
AssertBits(DoubleDouble.InvSqrt(value), DoubleDouble.RootN(value, -2));
|
||||
AssertBits(DoubleDouble.Cbrt(value), DoubleDouble.RootN(value, 3));
|
||||
@@ -153,7 +153,7 @@ public class DoubleDoubleRootNTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenericContractAndFacadeDispatchToTypeMembers()
|
||||
public void GenericContractDispatchesToTypeMembers()
|
||||
{
|
||||
DoubleDouble[] values = [DoubleDouble.Zero, DoubleDouble.NegativeZero, DoubleDouble.NaN,
|
||||
DoubleDouble.PositiveInfinity, DoubleDouble.NegativeInfinity, new(-2.0), new(double.Epsilon),
|
||||
@@ -167,7 +167,6 @@ public class DoubleDoubleRootNTests
|
||||
{
|
||||
DoubleDouble result = DoubleDouble.RootN(value, degree);
|
||||
AssertBits(result, GenericRootN(value, degree));
|
||||
AssertBits(result, DDMath.RootN(value, degree));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public class PreciseMathExpTests
|
||||
// Inputs are exact stored binary64 sums, not rounded irrational constants.
|
||||
DoubleDouble input = DoubleDouble.FromComponents(high, low);
|
||||
(Units(input.High) + Units(input.Low)).ShouldBe(Units(high) + Units(low));
|
||||
DoubleDouble actual = DDMath.Exp(input);
|
||||
DoubleDouble actual = DoubleDouble.Exp(input);
|
||||
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
|
||||
if (overflow)
|
||||
{
|
||||
@@ -60,7 +60,7 @@ public class PreciseMathExpTests
|
||||
{
|
||||
// Independently round Decimal.exp(exact binary64 input)/2^-1074 to an
|
||||
// integer at 180 digits. Pin more than the general one-subnormal bound.
|
||||
DoubleDouble actual = DDMath.Exp(new DoubleDouble(input));
|
||||
DoubleDouble actual = DoubleDouble.Exp(new DoubleDouble(input));
|
||||
BitConverter.DoubleToInt64Bits(actual.High).ShouldBe(expectedBits);
|
||||
BitConverter.DoubleToInt64Bits(actual.Low).ShouldBe(0L);
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public class PreciseMathExpTests
|
||||
[InlineData(709.781)]
|
||||
public void LegacyFiniteResultsDoNotOverflowPrematurely(double input)
|
||||
{
|
||||
DoubleDouble actual = DDMath.Exp(new DoubleDouble(input));
|
||||
DoubleDouble actual = DoubleDouble.Exp(new DoubleDouble(input));
|
||||
DoubleDouble.IsFinite(actual).ShouldBeTrue();
|
||||
actual.Low.ShouldNotBe(0.0);
|
||||
// Component-aware accuracy for these exact inputs is pinned by ReferenceCases.
|
||||
@@ -83,7 +83,7 @@ public class PreciseMathExpTests
|
||||
double.MaxValue, double.MinValue];
|
||||
foreach (double input in inputs)
|
||||
{
|
||||
DoubleDouble actual = DDMath.Exp(new DoubleDouble(input));
|
||||
DoubleDouble actual = DoubleDouble.Exp(new DoubleDouble(input));
|
||||
double expected = Math.Exp(input);
|
||||
BitConverter.DoubleToInt64Bits(actual.High).ShouldBe(
|
||||
BitConverter.DoubleToInt64Bits(double.IsNaN(expected) ? double.NaN : expected));
|
||||
|
||||
@@ -11,7 +11,7 @@ public class PreciseMathInvSqrtTests
|
||||
public void IrrationalInverseRootsRetainMoreThanBinary64Precision(double value)
|
||||
{
|
||||
DoubleDouble input = new(value);
|
||||
DoubleDouble actual = DDMath.InvSqrt(input);
|
||||
DoubleDouble actual = DoubleDouble.InvSqrt(input);
|
||||
actual.Low.ShouldNotBe(0.0);
|
||||
AssertInvSqrtBound(input, actual);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ public class PreciseMathInvSqrtTests
|
||||
double.NaN, -1.0, -double.Epsilon, double.MinValue];
|
||||
foreach (double value in values)
|
||||
{
|
||||
DoubleDouble actual = DDMath.InvSqrt(new DoubleDouble(value));
|
||||
DoubleDouble actual = DoubleDouble.InvSqrt(new DoubleDouble(value));
|
||||
// Binary64 is an independent oracle only for these special values.
|
||||
double expected = 1.0 / Math.Sqrt(value);
|
||||
BitConverter.DoubleToInt64Bits(actual.High).ShouldBe(
|
||||
@@ -33,7 +33,7 @@ public class PreciseMathInvSqrtTests
|
||||
}
|
||||
|
||||
DoubleDouble negative = DoubleDouble.FromComponents(-1.0, Math.ScaleB(1.0, -54));
|
||||
DoubleDouble.IsNaN(DDMath.InvSqrt(negative)).ShouldBeTrue();
|
||||
DoubleDouble.IsNaN(DoubleDouble.InvSqrt(negative)).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -42,7 +42,7 @@ public class PreciseMathInvSqrtTests
|
||||
// 1/sqrt(2^(2k)) = 2^-k exactly, including the minimum subnormal input.
|
||||
for (int exponent = -1074; exponent <= 1022; exponent += 2)
|
||||
{
|
||||
DoubleDouble actual = DDMath.InvSqrt(new DoubleDouble(Math.ScaleB(1.0, exponent)));
|
||||
DoubleDouble actual = DoubleDouble.InvSqrt(new DoubleDouble(Math.ScaleB(1.0, exponent)));
|
||||
actual.High.ShouldBe(Math.ScaleB(1.0, -(exponent / 2)));
|
||||
BitConverter.DoubleToInt64Bits(actual.Low).ShouldBe(0L);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class PreciseMathInvSqrtTests
|
||||
foreach (double low in new[] { Math.ScaleB(1.0, -54), -Math.ScaleB(1.0, -54) })
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(1.0, low);
|
||||
DoubleDouble actual = DDMath.InvSqrt(input);
|
||||
DoubleDouble actual = DoubleDouble.InvSqrt(input);
|
||||
actual.High.ShouldBe(1.0);
|
||||
Math.Sign(actual.Low).ShouldBe(-Math.Sign(low));
|
||||
AssertInvSqrtBound(input, actual);
|
||||
@@ -78,7 +78,7 @@ public class PreciseMathInvSqrtTests
|
||||
if (boundary > 0.0)
|
||||
{
|
||||
DoubleDouble input = new(boundary);
|
||||
AssertInvSqrtBound(input, DDMath.InvSqrt(input));
|
||||
AssertInvSqrtBound(input, DoubleDouble.InvSqrt(input));
|
||||
}
|
||||
}
|
||||
foreach (double residual in new[] { 0.0, low, -low, double.Epsilon, -double.Epsilon })
|
||||
@@ -86,7 +86,7 @@ public class PreciseMathInvSqrtTests
|
||||
DoubleDouble input = DoubleDouble.FromComponents(high, residual);
|
||||
if (input.High > 0.0)
|
||||
{
|
||||
AssertInvSqrtBound(input, DDMath.InvSqrt(input));
|
||||
AssertInvSqrtBound(input, DoubleDouble.InvSqrt(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public class PreciseMathInvSqrtTests
|
||||
DoubleDouble.FromComponents(0.0, double.Epsilon)];
|
||||
foreach (DoubleDouble input in boundaries)
|
||||
{
|
||||
AssertInvSqrtBound(input, DDMath.InvSqrt(input));
|
||||
AssertInvSqrtBound(input, DoubleDouble.InvSqrt(input));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ public class PreciseMathInvSqrtTests
|
||||
DoubleDouble input = DoubleDouble.FromComponents(high, low);
|
||||
if (DoubleDouble.IsFinite(input) && input.High > 0.0)
|
||||
{
|
||||
AssertInvSqrtBound(input, DDMath.InvSqrt(input));
|
||||
AssertInvSqrtBound(input, DoubleDouble.InvSqrt(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public class PreciseMathInvSqrtTests
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
double low = Math.ScaleB(sign, exponent);
|
||||
DoubleDouble actual = DDMath.InvSqrt(DoubleDouble.FromComponents(1.0, low));
|
||||
DoubleDouble actual = DoubleDouble.InvSqrt(DoubleDouble.FromComponents(1.0, low));
|
||||
actual.High.ShouldBe(1.0);
|
||||
actual.Low.ShouldBe(Math.ScaleB(-sign, exponent - 1));
|
||||
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
|
||||
|
||||
@@ -17,10 +17,10 @@ public class PreciseMathLogTests
|
||||
foreach (double value in values)
|
||||
{
|
||||
DoubleDouble expected = new(Math.Log(value));
|
||||
AssertBits(DDMath.Log(new DoubleDouble(value)), expected.High, 0.0);
|
||||
AssertBits(DoubleDouble.Log(new DoubleDouble(value)), expected.High, 0.0);
|
||||
}
|
||||
DoubleDouble negative = DoubleDouble.FromComponents(-1.0, Math.ScaleB(1.0, -54));
|
||||
AssertBits(DDMath.Log(negative), double.NaN, 0.0);
|
||||
AssertBits(DoubleDouble.Log(negative), double.NaN, 0.0);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -31,7 +31,7 @@ public class PreciseMathLogTests
|
||||
// 2200 digits and checked with Fraction. No binary64 log supplies the oracle.
|
||||
DoubleDouble input = DoubleDouble.FromComponents(high, low);
|
||||
(Units(input.High) + Units(input.Low)).ShouldBe(Units(high) + Units(low));
|
||||
DoubleDouble actual = DDMath.Log(input);
|
||||
DoubleDouble actual = DoubleDouble.Log(input);
|
||||
AssertReferenceBound(actual, reference, 1, $"{label}: ({high:R}, {low:R})");
|
||||
if (input == DoubleDouble.One)
|
||||
{
|
||||
@@ -51,7 +51,7 @@ public class PreciseMathLogTests
|
||||
for (int exponent = -1074; exponent <= 1023; ++exponent)
|
||||
{
|
||||
DoubleDouble input = new(Math.ScaleB(1.0, exponent));
|
||||
AssertReferenceBound(DDMath.Log(input), LogReferenceData.Ln2, exponent, $"2^{exponent}");
|
||||
AssertReferenceBound(DoubleDouble.Log(input), LogReferenceData.Ln2, exponent, $"2^{exponent}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class PreciseMathLogTests
|
||||
{
|
||||
// The legacy Log(1e308) formed u+value and overflowed. The finite
|
||||
// kernel must reduce the mantissa before constructing its denominator.
|
||||
DoubleDouble actual = DDMath.Log(new DoubleDouble(input));
|
||||
DoubleDouble actual = DoubleDouble.Log(new DoubleDouble(input));
|
||||
DoubleDouble.IsFinite(actual).ShouldBeTrue();
|
||||
actual.Low.ShouldNotBe(0.0);
|
||||
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
|
||||
@@ -75,12 +75,12 @@ public class PreciseMathLogTests
|
||||
{
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
AssertBits(DDMath.Log(DoubleDouble.FromComponents(1.0, sign * double.Epsilon)),
|
||||
AssertBits(DoubleDouble.Log(DoubleDouble.FromComponents(1.0, sign * double.Epsilon)),
|
||||
sign * double.Epsilon, 0.0);
|
||||
double delta = sign * Math.ScaleB(1.0, -500);
|
||||
// ln(1+d) = d-d^2/2+O(d^3). Here the cubic tail is below half
|
||||
// the minimum subnormal, so these two binary components are exact.
|
||||
AssertBits(DDMath.Log(DoubleDouble.FromComponents(1.0, delta)),
|
||||
AssertBits(DoubleDouble.Log(DoubleDouble.FromComponents(1.0, delta)),
|
||||
delta, -Math.ScaleB(1.0, -1001));
|
||||
}
|
||||
}
|
||||
@@ -89,8 +89,8 @@ public class PreciseMathLogTests
|
||||
public void ArbitraryPairsAreNormalizedByThePublicFactoryBeforeLog()
|
||||
{
|
||||
DoubleDouble positive = DoubleDouble.FromComponents(0.0, 2.0);
|
||||
AssertReferenceBound(DDMath.Log(positive), LogReferenceData.Ln2, 1, "zero-high public construction");
|
||||
AssertBits(DDMath.Log(DoubleDouble.FromComponents(0.0, -2.0)), double.NaN, 0.0);
|
||||
AssertReferenceBound(DoubleDouble.Log(positive), LogReferenceData.Ln2, 1, "zero-high public construction");
|
||||
AssertBits(DoubleDouble.Log(DoubleDouble.FromComponents(0.0, -2.0)), double.NaN, 0.0);
|
||||
}
|
||||
|
||||
private static void AssertReferenceBound(DoubleDouble actual, string reference, int factor, string context)
|
||||
|
||||
@@ -6,17 +6,17 @@ public class PreciseMathPowTests
|
||||
public void NegativePowerDoesNotOverflowBeforeReciprocation()
|
||||
{
|
||||
// Legacy review: Pow(2, -1024) overflowed before taking the reciprocal.
|
||||
DoubleDouble actual = DDMath.Pow(new DoubleDouble(2.0), -1024);
|
||||
DoubleDouble actual = DoubleDouble.Pow(new DoubleDouble(2.0), -1024);
|
||||
AssertBits(actual, Math.ScaleB(1.0, -1024), 0.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MinimumIntegerExponentDoesNotOverflowItsMagnitude()
|
||||
{
|
||||
AssertBits(DDMath.Pow(DoubleDouble.One, int.MinValue), 1.0, 0.0);
|
||||
AssertBits(DDMath.Pow(DoubleDouble.NegativeOne, int.MinValue), 1.0, 0.0);
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(2.0), int.MinValue), 0.0, 0.0);
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(0.5), int.MinValue), double.PositiveInfinity, 0.0);
|
||||
AssertBits(DoubleDouble.Pow(DoubleDouble.One, int.MinValue), 1.0, 0.0);
|
||||
AssertBits(DoubleDouble.Pow(DoubleDouble.NegativeOne, int.MinValue), 1.0, 0.0);
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(2.0), int.MinValue), 0.0, 0.0);
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(0.5), int.MinValue), double.PositiveInfinity, 0.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -25,7 +25,7 @@ public class PreciseMathPowTests
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(sign * double.MaxValue, sign * double.Epsilon);
|
||||
AssertBits(DDMath.Pow(input, 1), input.High, input.Low);
|
||||
AssertBits(DoubleDouble.Pow(input, 1), input.High, input.Low);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class PreciseMathPowTests
|
||||
// Every int converts exactly to double. BCL is an independent
|
||||
// special-value/sign oracle, not the finite precision oracle.
|
||||
double expected = Math.Pow(input, exponent);
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(input), exponent),
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(input), exponent),
|
||||
double.IsNaN(expected) ? double.NaN : expected, 0.0);
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ public class PreciseMathPowTests
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
double expected = sign < 0.0 && (power & 1) != 0 ? -magnitude : magnitude;
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(sign * Math.ScaleB(1.0, inputExponent)), power),
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(sign * Math.ScaleB(1.0, inputExponent)), power),
|
||||
expected, 0.0);
|
||||
}
|
||||
}
|
||||
@@ -118,13 +118,13 @@ public class PreciseMathPowTests
|
||||
AssertExactPowerBound(input, -1);
|
||||
if (scale == -500 || scale == 500)
|
||||
{
|
||||
DoubleDouble actual = DDMath.Pow(input, -2);
|
||||
DoubleDouble actual = DoubleDouble.Pow(input, -2);
|
||||
actual.Low.ShouldNotBe(0.0);
|
||||
AssertExactPowerBound(input, -2);
|
||||
}
|
||||
if (scale < 0)
|
||||
{
|
||||
DDMath.Pow(input, -1).Low.ShouldNotBe(0.0);
|
||||
DoubleDouble.Pow(input, -1).Low.ShouldNotBe(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,11 +141,11 @@ public class PreciseMathPowTests
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
DoubleDouble tiny = DoubleDouble.FromComponents(Math.ScaleB(1.0, -500), sign * double.Epsilon);
|
||||
AssertBits(DDMath.Pow(tiny, -1), Math.ScaleB(1.0, 500), -sign * Math.ScaleB(1.0, -74));
|
||||
AssertBits(DoubleDouble.Pow(tiny, -1), Math.ScaleB(1.0, 500), -sign * Math.ScaleB(1.0, -74));
|
||||
DoubleDouble huge = DoubleDouble.FromComponents(Math.ScaleB(1.0, 500), sign * Math.ScaleB(1.0, -74));
|
||||
AssertBits(DDMath.Pow(huge, -1), Math.ScaleB(1.0, -500), -sign * double.Epsilon);
|
||||
AssertBits(DoubleDouble.Pow(huge, -1), Math.ScaleB(1.0, -500), -sign * double.Epsilon);
|
||||
DoubleDouble tinySquare = DoubleDouble.FromComponents(Math.ScaleB(1.0, -250), sign * double.Epsilon);
|
||||
AssertBits(DDMath.Pow(tinySquare, -2), Math.ScaleB(1.0, 500), -sign * Math.ScaleB(1.0, -323));
|
||||
AssertBits(DoubleDouble.Pow(tinySquare, -2), Math.ScaleB(1.0, 500), -sign * Math.ScaleB(1.0, -323));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ public class PreciseMathPowTests
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(sign * Math.ScaleB(1.0, scale),
|
||||
sign * lowSign * Math.ScaleB(1.0, scale - 54));
|
||||
AssertBits(DDMath.Pow(input, exponent), sign * (aboveTie ? double.Epsilon : 0.0), 0.0);
|
||||
AssertBits(DoubleDouble.Pow(input, exponent), sign * (aboveTie ? double.Epsilon : 0.0), 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,7 +183,7 @@ public class PreciseMathPowTests
|
||||
AssertExactPowerBound(finite, exponent);
|
||||
DoubleDouble overflowing = DoubleDouble.FromComponents(Math.ScaleB(1.0, scale),
|
||||
lowSign * Math.ScaleB(1.0, scale - 57));
|
||||
AssertBits(DDMath.Pow(overflowing, exponent), double.PositiveInfinity, 0.0);
|
||||
AssertBits(DoubleDouble.Pow(overflowing, exponent), double.PositiveInfinity, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ public class PreciseMathPowTests
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(sign * high, sign * low);
|
||||
DoubleDouble actual = DDMath.Pow(input, exponent);
|
||||
DoubleDouble actual = DoubleDouble.Pow(input, exponent);
|
||||
actual.Low.ShouldNotBe(0.0);
|
||||
int resultSign = sign < 0.0 && (exponent & 1) != 0 ? -1 : 1;
|
||||
// Check both interval endpoints so the reference uncertainty is not
|
||||
@@ -239,7 +239,7 @@ public class PreciseMathPowTests
|
||||
{
|
||||
(numerator, denominator) = (denominator * numerator.Sign, BigInteger.Abs(numerator));
|
||||
}
|
||||
AssertRationalBound(DDMath.Pow(input, exponent), numerator, denominator, exponent,
|
||||
AssertRationalBound(DoubleDouble.Pow(input, exponent), numerator, denominator, exponent,
|
||||
$"({input.High:R}, {input.Low:R})^{exponent}");
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ public class PreciseMathRealPowSpecialTests
|
||||
DoubleDouble.FromComponents(double.MaxValue, double.Epsilon)];
|
||||
foreach (DoubleDouble value in values)
|
||||
{
|
||||
AssertBits(DDMath.Pow(value, 0.0), DoubleDouble.One);
|
||||
AssertBits(DDMath.Pow(value, new DoubleDouble(-0.0)), DoubleDouble.One);
|
||||
AssertBits(DDMath.Pow(value, 1.0), value);
|
||||
AssertBits(DDMath.Pow(value, DoubleDouble.One), value);
|
||||
AssertBits(DoubleDouble.Pow(value, 0.0), DoubleDouble.One);
|
||||
AssertBits(DoubleDouble.Pow(value, new DoubleDouble(-0.0)), DoubleDouble.One);
|
||||
AssertBits(DoubleDouble.Pow(value, 1.0), value);
|
||||
AssertBits(DoubleDouble.Pow(value, DoubleDouble.One), value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ public class PreciseMathRealPowSpecialTests
|
||||
continue;
|
||||
}
|
||||
DoubleDouble input = new(value);
|
||||
AssertBits(DDMath.Pow(input, exponent), new DoubleDouble(expected));
|
||||
AssertBits(DDMath.Pow(input, new DoubleDouble(exponent)), new DoubleDouble(expected));
|
||||
AssertBits(DoubleDouble.Pow(input, exponent), new DoubleDouble(expected));
|
||||
AssertBits(DoubleDouble.Pow(input, new DoubleDouble(exponent)), new DoubleDouble(expected));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,27 +53,27 @@ public class PreciseMathRealPowSpecialTests
|
||||
DoubleDouble odd = DoubleDouble.FromComponents(direction * high, 1.0);
|
||||
DoubleDouble even = DoubleDouble.FromComponents(direction * high, 2.0);
|
||||
DoubleDouble fractional = DoubleDouble.FromComponents(direction * high, 0.5);
|
||||
AssertBits(DDMath.Pow(DoubleDouble.NegativeOne, odd), DoubleDouble.NegativeOne);
|
||||
AssertBits(DDMath.Pow(DoubleDouble.NegativeOne, even), DoubleDouble.One);
|
||||
AssertBits(DDMath.Pow(DoubleDouble.NegativeOne, fractional), DoubleDouble.NaN);
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(-2.0), odd),
|
||||
AssertBits(DoubleDouble.Pow(DoubleDouble.NegativeOne, odd), DoubleDouble.NegativeOne);
|
||||
AssertBits(DoubleDouble.Pow(DoubleDouble.NegativeOne, even), DoubleDouble.One);
|
||||
AssertBits(DoubleDouble.Pow(DoubleDouble.NegativeOne, fractional), DoubleDouble.NaN);
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(-2.0), odd),
|
||||
new DoubleDouble(direction > 0.0 ? double.NegativeInfinity : -0.0));
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(-0.5), odd),
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(-0.5), odd),
|
||||
new DoubleDouble(direction > 0.0 ? -0.0 : double.NegativeInfinity));
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(-2.0), even),
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(-2.0), even),
|
||||
new DoubleDouble(direction > 0.0 ? double.PositiveInfinity : 0.0));
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(-0.0), odd),
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(-0.0), odd),
|
||||
new DoubleDouble(direction > 0.0 ? -0.0 : double.NegativeInfinity));
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(double.NegativeInfinity), odd),
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(double.NegativeInfinity), odd),
|
||||
new DoubleDouble(direction > 0.0 ? double.NegativeInfinity : -0.0));
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(-0.0), fractional),
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(-0.0), fractional),
|
||||
new DoubleDouble(direction > 0.0 ? 0.0 : double.PositiveInfinity));
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(double.NegativeInfinity), fractional),
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(double.NegativeInfinity), fractional),
|
||||
new DoubleDouble(direction > 0.0 ? double.PositiveInfinity : 0.0));
|
||||
}
|
||||
}
|
||||
DoubleDouble belowInteger = DoubleDouble.FromComponents(3.0, -double.Epsilon);
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(-2.0), belowInteger), DoubleDouble.NaN);
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(-2.0), belowInteger), DoubleDouble.NaN);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -84,9 +84,9 @@ public class PreciseMathRealPowSpecialTests
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
DoubleDouble value = DoubleDouble.FromComponents(sign, sign * low);
|
||||
AssertBits(DDMath.Pow(value, double.PositiveInfinity),
|
||||
AssertBits(DoubleDouble.Pow(value, double.PositiveInfinity),
|
||||
new DoubleDouble(low > 0.0 ? double.PositiveInfinity : 0.0));
|
||||
AssertBits(DDMath.Pow(value, new DoubleDouble(double.NegativeInfinity)),
|
||||
AssertBits(DoubleDouble.Pow(value, new DoubleDouble(double.NegativeInfinity)),
|
||||
new DoubleDouble(low < 0.0 ? double.PositiveInfinity : 0.0));
|
||||
}
|
||||
}
|
||||
@@ -102,11 +102,11 @@ public class PreciseMathRealPowSpecialTests
|
||||
{
|
||||
foreach (int exponent in exponents)
|
||||
{
|
||||
DoubleDouble expected = DDMath.Pow(value, exponent);
|
||||
DoubleDouble expected = DoubleDouble.Pow(value, exponent);
|
||||
// Dispatch consistency, not an accuracy oracle: the integer suite
|
||||
// separately checks these algorithms against independent references.
|
||||
AssertBits(DDMath.Pow(value, (double)exponent), expected);
|
||||
AssertBits(DDMath.Pow(value, new DoubleDouble(exponent)), expected);
|
||||
AssertBits(DoubleDouble.Pow(value, (double)exponent), expected);
|
||||
AssertBits(DoubleDouble.Pow(value, new DoubleDouble(exponent)), expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,8 +116,8 @@ public class PreciseMathRealPowSpecialTests
|
||||
{
|
||||
// Independently rounded sqrt(2) binary64 components; also used by the
|
||||
// constant-reference generator. Compare components, not high+low in double.
|
||||
foreach (DoubleDouble actual in new[] { DDMath.Pow(new DoubleDouble(2.0), 0.5),
|
||||
DDMath.Pow(new DoubleDouble(2.0), new DoubleDouble(0.5)) })
|
||||
foreach (DoubleDouble actual in new[] { DoubleDouble.Pow(new DoubleDouble(2.0), 0.5),
|
||||
DoubleDouble.Pow(new DoubleDouble(2.0), new DoubleDouble(0.5)) })
|
||||
{
|
||||
actual.High.ShouldBe(1.4142135623730951);
|
||||
Math.Abs(actual.Low - -9.667293313452913e-17).ShouldBeLessThan(Math.ScaleB(1.0, -90));
|
||||
@@ -132,8 +132,8 @@ public class PreciseMathRealPowSpecialTests
|
||||
{
|
||||
// 4^-537.25 = 2^-1074.5 lies above the half-epsilon threshold;
|
||||
// 4^-537.75 = 2^-1075.5 lies below it. These are well separated from ties.
|
||||
foreach (DoubleDouble actual in new[] { DDMath.Pow(new DoubleDouble(4.0), exponent),
|
||||
DDMath.Pow(new DoubleDouble(4.0), new DoubleDouble(exponent)) })
|
||||
foreach (DoubleDouble actual in new[] { DoubleDouble.Pow(new DoubleDouble(4.0), exponent),
|
||||
DoubleDouble.Pow(new DoubleDouble(4.0), new DoubleDouble(exponent)) })
|
||||
{
|
||||
BitConverter.DoubleToInt64Bits(actual.High).ShouldBe(expectedBits);
|
||||
BitConverter.DoubleToInt64Bits(actual.Low).ShouldBe(0L);
|
||||
@@ -149,8 +149,8 @@ public class PreciseMathRealPowSpecialTests
|
||||
{
|
||||
bool grows = (value > 1.0) == (exponent > 0.0);
|
||||
DoubleDouble expected = new(grows ? double.PositiveInfinity : 0.0);
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(value), exponent), expected);
|
||||
AssertBits(DDMath.Pow(new DoubleDouble(value), new DoubleDouble(exponent)), expected);
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(value), exponent), expected);
|
||||
AssertBits(DoubleDouble.Pow(new DoubleDouble(value), new DoubleDouble(exponent)), expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public class PreciseMathRealPowTests
|
||||
(Units(input.High) + Units(input.Low)).ShouldBe(Units(high) + Units(low));
|
||||
BigInteger exponentUnits = Units(exponentHigh) + Units(scalar ? 0.0 : exponentLow);
|
||||
(Units(exponent.High) + Units(exponent.Low)).ShouldBe(exponentUnits);
|
||||
DoubleDouble actual = scalar ? DDMath.Pow(input, exponentHigh) : DDMath.Pow(input, exponent);
|
||||
DoubleDouble actual = scalar ? DoubleDouble.Pow(input, exponentHigh) : DoubleDouble.Pow(input, exponent);
|
||||
string context = $"{label}, scalar={scalar}: ({high:R}, {low:R})^({exponentHigh:R}, {exponentLow:R})";
|
||||
AssertReferenceBound(actual, reference, exponentUnits, context);
|
||||
}
|
||||
@@ -36,9 +36,9 @@ public class PreciseMathRealPowTests
|
||||
// These references are exact rational identities. The general ln/exp
|
||||
// implementation is approximate, so do not require universal bit equality.
|
||||
string reference = expected.ToString("e17", CultureInfo.InvariantCulture);
|
||||
AssertReferenceBound(DDMath.Pow(new DoubleDouble(input), exponent), reference,
|
||||
AssertReferenceBound(DoubleDouble.Pow(new DoubleDouble(input), exponent), reference,
|
||||
Units(exponent), "scalar exact binary reference");
|
||||
AssertReferenceBound(DDMath.Pow(new DoubleDouble(input), new DoubleDouble(exponent)), reference,
|
||||
AssertReferenceBound(DoubleDouble.Pow(new DoubleDouble(input), new DoubleDouble(exponent)), reference,
|
||||
Units(exponent), "DD exact binary reference");
|
||||
}
|
||||
|
||||
@@ -55,17 +55,17 @@ public class PreciseMathRealPowTests
|
||||
{
|
||||
foreach (int exponent in new[] { -63, -7, -1, 0, 1, 7, 63 })
|
||||
{
|
||||
DoubleDouble expected = DDMath.Pow(input, exponent);
|
||||
AssertBits(DDMath.Pow(input, (double)exponent), expected);
|
||||
AssertBits(DDMath.Pow(input, new DoubleDouble(exponent)), expected);
|
||||
DoubleDouble expected = DoubleDouble.Pow(input, exponent);
|
||||
AssertBits(DoubleDouble.Pow(input, (double)exponent), expected);
|
||||
AssertBits(DoubleDouble.Pow(input, new DoubleDouble(exponent)), expected);
|
||||
}
|
||||
}
|
||||
foreach (int exponent in new[] { int.MinValue, int.MaxValue })
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(1.0, Math.ScaleB(1.0, -54));
|
||||
DoubleDouble expected = DDMath.Pow(input, exponent);
|
||||
AssertBits(DDMath.Pow(input, (double)exponent), expected);
|
||||
AssertBits(DDMath.Pow(input, new DoubleDouble(exponent)), expected);
|
||||
DoubleDouble expected = DoubleDouble.Pow(input, exponent);
|
||||
AssertBits(DoubleDouble.Pow(input, (double)exponent), expected);
|
||||
AssertBits(DoubleDouble.Pow(input, new DoubleDouble(exponent)), expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ public class PreciseMathRealPowTests
|
||||
foreach (double low in new[] { double.Epsilon, -double.Epsilon })
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(high, low);
|
||||
AssertBits(DDMath.Pow(input, 1.0), input);
|
||||
AssertBits(DDMath.Pow(input, DoubleDouble.One), input);
|
||||
AssertBits(DoubleDouble.Pow(input, 1.0), input);
|
||||
AssertBits(DoubleDouble.Pow(input, DoubleDouble.One), input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public class PreciseMathReciprocalTests
|
||||
foreach (double low in new[] { 0.0, Math.ScaleB(1.0, -54), -Math.ScaleB(1.0, -54) })
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(high, low);
|
||||
DoubleDouble actual = DDMath.Reciprocal(input);
|
||||
DoubleDouble actual = DoubleDouble.Reciprocal(input);
|
||||
actual.Low.ShouldNotBe(0.0);
|
||||
AssertReciprocalBound(input, actual);
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public class PreciseMathReciprocalTests
|
||||
double.NaN, BitConverter.Int64BitsToDouble(0x7ff0000000000001L)];
|
||||
foreach (double value in values)
|
||||
{
|
||||
DoubleDouble actual = DDMath.Reciprocal(new DoubleDouble(value));
|
||||
DoubleDouble actual = DoubleDouble.Reciprocal(new DoubleDouble(value));
|
||||
// Binary64 is an independent oracle for special values only.
|
||||
double expected = 1.0 / value;
|
||||
BitConverter.DoubleToInt64Bits(actual.High).ShouldBe(
|
||||
@@ -44,7 +44,7 @@ public class PreciseMathReciprocalTests
|
||||
{
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
DoubleDouble actual = DDMath.Reciprocal(new DoubleDouble(Math.ScaleB(sign, exponent)));
|
||||
DoubleDouble actual = DoubleDouble.Reciprocal(new DoubleDouble(Math.ScaleB(sign, exponent)));
|
||||
actual.High.ShouldBe(Math.ScaleB(sign, -exponent));
|
||||
BitConverter.DoubleToInt64Bits(actual.Low).ShouldBe(0L);
|
||||
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
|
||||
@@ -104,7 +104,7 @@ public class PreciseMathReciprocalTests
|
||||
{
|
||||
double low = Math.ScaleB(lowSign, exponent);
|
||||
DoubleDouble input = DoubleDouble.FromComponents(sign, low);
|
||||
DoubleDouble actual = DDMath.Reciprocal(input);
|
||||
DoubleDouble actual = DoubleDouble.Reciprocal(input);
|
||||
actual.High.ShouldBe(sign);
|
||||
actual.Low.ShouldBe(-low);
|
||||
AssertReciprocalBound(input, actual);
|
||||
@@ -116,7 +116,7 @@ public class PreciseMathReciprocalTests
|
||||
private static void AssertMatchesDivision(DoubleDouble input)
|
||||
{
|
||||
DoubleDouble expected = 1.0 / input;
|
||||
DoubleDouble actual = DDMath.Reciprocal(input);
|
||||
DoubleDouble actual = DoubleDouble.Reciprocal(input);
|
||||
string context = $"Input: ({input.High:R}, {input.Low:R})";
|
||||
BitConverter.DoubleToInt64Bits(actual.High).ShouldBe(BitConverter.DoubleToInt64Bits(expected.High), context);
|
||||
BitConverter.DoubleToInt64Bits(actual.Low).ShouldBe(BitConverter.DoubleToInt64Bits(expected.Low), context);
|
||||
|
||||
@@ -10,7 +10,7 @@ public class PreciseMathSqrtTests
|
||||
[InlineData(2.25, 1.5)]
|
||||
public void ExactBinarySquaresHaveExactRoots(double square, double root)
|
||||
{
|
||||
DoubleDouble actual = DDMath.Sqrt(new DoubleDouble(square));
|
||||
DoubleDouble actual = DoubleDouble.Sqrt(new DoubleDouble(square));
|
||||
actual.High.ShouldBe(root);
|
||||
BitConverter.DoubleToInt64Bits(actual.Low).ShouldBe(0L);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public class PreciseMathSqrtTests
|
||||
// sqrt(2^(2k)) = 2^k exactly, including the minimum subnormal input.
|
||||
for (int exponent = -1074; exponent <= 1022; exponent += 2)
|
||||
{
|
||||
DoubleDouble actual = DDMath.Sqrt(new DoubleDouble(Math.ScaleB(1.0, exponent)));
|
||||
DoubleDouble actual = DoubleDouble.Sqrt(new DoubleDouble(Math.ScaleB(1.0, exponent)));
|
||||
actual.High.ShouldBe(Math.ScaleB(1.0, exponent / 2));
|
||||
BitConverter.DoubleToInt64Bits(actual.Low).ShouldBe(0L);
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public class PreciseMathSqrtTests
|
||||
public void IrrationalRootsRetainMoreThanBinary64Precision(double value)
|
||||
{
|
||||
DoubleDouble input = new(value);
|
||||
DoubleDouble actual = DDMath.Sqrt(input);
|
||||
DoubleDouble actual = DoubleDouble.Sqrt(input);
|
||||
actual.Low.ShouldNotBe(0.0);
|
||||
AssertSqrtBound(input, actual);
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public class PreciseMathSqrtTests
|
||||
foreach (double low in new[] { Math.ScaleB(1.0, -54), -Math.ScaleB(1.0, -54) })
|
||||
{
|
||||
DoubleDouble input = DoubleDouble.FromComponents(1.0, low);
|
||||
DoubleDouble actual = DDMath.Sqrt(input);
|
||||
DoubleDouble actual = DoubleDouble.Sqrt(input);
|
||||
actual.High.ShouldBe(1.0);
|
||||
(Math.Sign(actual.Low) == Math.Sign(low)).ShouldBeTrue();
|
||||
AssertSqrtBound(input, actual);
|
||||
@@ -61,7 +61,7 @@ public class PreciseMathSqrtTests
|
||||
double.NaN, -1.0, -double.Epsilon, double.MinValue];
|
||||
foreach (double value in values)
|
||||
{
|
||||
DoubleDouble actual = DDMath.Sqrt(new DoubleDouble(value));
|
||||
DoubleDouble actual = DoubleDouble.Sqrt(new DoubleDouble(value));
|
||||
double expected = Math.Sqrt(value);
|
||||
// Construction canonicalizes all NaNs to double.NaN, including
|
||||
// domain errors; zeros must instead be compared by their sign bits.
|
||||
@@ -72,7 +72,7 @@ public class PreciseMathSqrtTests
|
||||
}
|
||||
|
||||
DoubleDouble negative = DoubleDouble.FromComponents(-1.0, Math.ScaleB(1.0, -54));
|
||||
DoubleDouble.IsNaN(DDMath.Sqrt(negative)).ShouldBeTrue();
|
||||
DoubleDouble.IsNaN(DoubleDouble.Sqrt(negative)).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -92,7 +92,7 @@ public class PreciseMathSqrtTests
|
||||
if (boundary > 0.0)
|
||||
{
|
||||
DoubleDouble input = new(boundary);
|
||||
AssertSqrtBound(input, DDMath.Sqrt(input));
|
||||
AssertSqrtBound(input, DoubleDouble.Sqrt(input));
|
||||
}
|
||||
}
|
||||
foreach (double residual in new[] { 0.0, low, -low, double.Epsilon, -double.Epsilon })
|
||||
@@ -100,7 +100,7 @@ public class PreciseMathSqrtTests
|
||||
DoubleDouble input = DoubleDouble.FromComponents(high, residual);
|
||||
if (input.High > 0.0)
|
||||
{
|
||||
AssertSqrtBound(input, DDMath.Sqrt(input));
|
||||
AssertSqrtBound(input, DoubleDouble.Sqrt(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ public class PreciseMathSqrtTests
|
||||
DoubleDouble.FromComponents(4.0, -Math.ScaleB(1.0, -52))];
|
||||
foreach (DoubleDouble input in boundaries)
|
||||
{
|
||||
AssertSqrtBound(input, DDMath.Sqrt(input));
|
||||
AssertSqrtBound(input, DoubleDouble.Sqrt(input));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ public class PreciseMathSqrtTests
|
||||
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));
|
||||
AssertSqrtBound(input, DoubleDouble.Sqrt(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,9 +156,9 @@ public class PreciseMathSqrtTests
|
||||
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));
|
||||
AssertSqrtBound(input, DoubleDouble.Sqrt(input));
|
||||
}
|
||||
DoubleDouble.IsNaN(DDMath.Sqrt(DoubleDouble.FromComponents(0.0, -1.0))).ShouldBeTrue();
|
||||
DoubleDouble.IsNaN(DoubleDouble.Sqrt(DoubleDouble.FromComponents(0.0, -1.0))).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -177,7 +177,7 @@ public class PreciseMathSqrtTests
|
||||
DoubleDouble input = DoubleDouble.FromComponents(high, low);
|
||||
if (DoubleDouble.IsFinite(input) && input.High > 0.0)
|
||||
{
|
||||
AssertSqrtBound(input, DDMath.Sqrt(input));
|
||||
AssertSqrtBound(input, DoubleDouble.Sqrt(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,7 +194,7 @@ public class PreciseMathSqrtTests
|
||||
foreach (double sign in new[] { -1.0, 1.0 })
|
||||
{
|
||||
double low = Math.ScaleB(sign, exponent);
|
||||
DoubleDouble actual = DDMath.Sqrt(DoubleDouble.FromComponents(1.0, low));
|
||||
DoubleDouble actual = DoubleDouble.Sqrt(DoubleDouble.FromComponents(1.0, low));
|
||||
actual.High.ShouldBe(1.0);
|
||||
actual.Low.ShouldBe(Math.ScaleB(sign, exponent - 1));
|
||||
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
|
||||
@@ -358,9 +358,6 @@ public class PreciseMathSqrtTests
|
||||
DoubleDouble actual = DoubleDouble.Sqrt(input);
|
||||
actual.High.ShouldBe(high);
|
||||
BitConverter.DoubleToInt64Bits(actual.Low).ShouldBe(BitConverter.DoubleToInt64Bits(low));
|
||||
DoubleDouble facade = DDMath.Sqrt(input);
|
||||
BitConverter.DoubleToInt64Bits(facade.High).ShouldBe(BitConverter.DoubleToInt64Bits(actual.High));
|
||||
BitConverter.DoubleToInt64Bits(facade.Low).ShouldBe(BitConverter.DoubleToInt64Bits(actual.Low));
|
||||
AssertSqrtBound(input, actual);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ public class PreciseMathTests
|
||||
foreach (double scalar in scalars)
|
||||
{
|
||||
// Binary64 is an independent reference when the input has no residual.
|
||||
DoubleDouble actual = DDMath.Abs(new DoubleDouble(scalar));
|
||||
DoubleDouble actual = DoubleDouble.Abs(new DoubleDouble(scalar));
|
||||
AssertBits(actual, double.IsNaN(scalar) ? double.NaN : Math.Abs(scalar), 0.0);
|
||||
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
|
||||
}
|
||||
@@ -19,8 +19,8 @@ public class PreciseMathTests
|
||||
{
|
||||
DoubleDouble positive = DoubleDouble.FromComponents(1.0, low);
|
||||
DoubleDouble negative = DoubleDouble.FromComponents(-1.0, -low);
|
||||
AssertBits(DDMath.Abs(positive), 1.0, low);
|
||||
AssertBits(DDMath.Abs(negative), 1.0, low);
|
||||
AssertBits(DoubleDouble.Abs(positive), 1.0, low);
|
||||
AssertBits(DoubleDouble.Abs(negative), 1.0, low);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user