added root functions
.NET Test / .NET tests (push) Successful in 1m31s

This commit is contained in:
2026-09-16 20:24:08 +04:00
parent 24d2aef892
commit 7084fae174
11 changed files with 1912 additions and 94 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ public static partial class DDMath
if (exponent < 0) if (exponent < 0)
{ {
// Reciprocate the bounded significand, NOT the range-limited result. // Reciprocate the bounded significand, NOT the range-limited result.
result = DoubleDouble.One / result; result = 1.0 / result;
resultExponent = -resultExponent; resultExponent = -resultExponent;
} }
return ScalePowerOfTwo(negative ? -result : result, resultExponent); return ScalePowerOfTwo(negative ? -result : result, resultExponent);
+32 -86
View File
@@ -48,100 +48,46 @@ public static partial class DDMath
return new DoubleDouble(high, low == 0.0 ? 0.0 : low); return new DoubleDouble(high, low == 0.0 ? 0.0 : low);
} }
/// <summary>Returns the nonnegative square root with double-double precision.</summary> /// <inheritdoc cref="DoubleDouble.Cbrt"/>
/// <remarks> [Pure]
/// Uses power-of-two scaling and an FMA-based Newton correction. Results are public static DoubleDouble Cbrt(DoubleDouble value)
/// approximate, not guaranteed correctly rounded; finite positive inputs are {
/// tested against a relative error bound of 2^-100 across the binary64 range. return DoubleDouble.Cbrt(value);
/// Signed zero and positive infinity are preserved. Negative nonzero values }
/// (including negative infinity) and NaN return canonical NaN.
/// </remarks> /// <inheritdoc cref="DoubleDouble.Hypot(DoubleDouble, DoubleDouble)"/>
[Pure]
public static DoubleDouble Hypot(DoubleDouble x, DoubleDouble y)
{
return DoubleDouble.Hypot(x, y);
}
/// <inheritdoc cref="DoubleDouble.Hypot(DoubleDouble, DoubleDouble, DoubleDouble)"/>
[Pure]
public static DoubleDouble Hypot(DoubleDouble x, DoubleDouble y, DoubleDouble z)
{
return DoubleDouble.Hypot(x, y, z);
}
/// <inheritdoc cref="DoubleDouble.RootN"/>
[Pure]
public static DoubleDouble RootN(DoubleDouble value, int n)
{
return DoubleDouble.RootN(value, n);
}
/// <inheritdoc cref="DoubleDouble.Sqrt"/>
[Pure] [Pure]
public static DoubleDouble Sqrt(DoubleDouble value) public static DoubleDouble Sqrt(DoubleDouble value)
{ {
if (double.IsNaN(value.High) || value.High < 0.0) return DoubleDouble.Sqrt(value);
{
return DoubleDouble.NaN;
}
if (value.High == 0.0 || double.IsPositiveInfinity(value.High))
{
return value;
}
// Choose an even exponent, rounding negative odd exponents down too.
// For positive finite normalized inputs, exponent is in [-1074, 1022]
// and the scaled high is in [1, 4). Scaling the high is exact; any
// underflow in a very sparse low is far below the relative error bound.
int exponent = Math.ILogB(value.High) & ~1;
double high = Math.ScaleB(value.High, -exponent);
double low = Math.ScaleB(value.Low, -exponent);
double estimate = Math.Sqrt(high);
// Estimate is in [1, 2]. FMA avoids rounding its square before
// cancellation. One Newton correction reduces O(u) error to O(u^2),
// where u=2^-53; both components of the input contribute to the residual.
double residual = Math.FusedMultiplyAdd(-estimate, estimate, high) + low;
double correction = residual / (2.0 * estimate);
DoubleDouble root = DoubleDouble.FromComponents(estimate, correction);
// The root high stays normal, even if rounding reaches the next binade.
// Normalize again to canonicalize a zero low, including any underflow
// of an exceptionally sparse correction during rescaling.
int rootExponent = exponent / 2;
return DoubleDouble.FromComponents(Math.ScaleB(root.High, rootExponent), Math.ScaleB(root.Low, rootExponent));
} }
/// <summary>Returns the reciprocal square root with double-double precision.</summary> /// <inheritdoc cref="DoubleDouble.InvSqrt"/>
/// <remarks>
/// Uses power-of-two scaling and a compensated Newton step without double-double
/// division. Results are approximate, not guaranteed correctly rounded; positive
/// finite inputs are tested against a relative error bound of 2^-100 across the
/// binary64 range. Positive/negative zero maps to positive/negative infinity,
/// positive infinity to positive zero, and negative nonzero values or NaN to
/// canonical NaN.
/// </remarks>
[Pure] [Pure]
public static DoubleDouble InvSqrt(DoubleDouble value) public static DoubleDouble InvSqrt(DoubleDouble value)
{ {
if (double.IsNaN(value.High) || value.High < 0.0) return DoubleDouble.InvSqrt(value);
{
return DoubleDouble.NaN;
}
if (value.High == 0.0)
{
return new DoubleDouble(Math.CopySign(double.PositiveInfinity, value.High));
}
if (double.IsPositiveInfinity(value.High))
{
return DoubleDouble.Zero;
}
// As in Sqrt, the even exponent is in [-1074, 1022], high is in [1, 4),
// and any underflow in a sparse scaled low is below the error bound.
int exponent = Math.ILogB(value.High) & ~1;
double high = Math.ScaleB(value.High, -exponent);
double low = Math.ScaleB(value.Low, -exponent);
double estimate = 1.0 / Math.Sqrt(high);
// With u=2^-53, the seed has O(u) relative error. Preserve the square's
// FMA residual before cancellation in 1 - (high + low)*estimate^2:
// using only the rounded square would leave O(u) error after refinement.
// All leading products are bounded and normal. Omitting low*squareError
// contributes only O(u^2), as does one Newton step's remaining error.
double square = estimate * estimate;
double squareError = Math.FusedMultiplyAdd(estimate, estimate, -square);
double residual = Math.FusedMultiplyAdd(-high, square, 1.0);
residual = Math.FusedMultiplyAdd(-high, squareError, residual);
residual = Math.FusedMultiplyAdd(-low, square, residual);
double correction = (0.5 * estimate) * residual;
DoubleDouble inverseRoot = DoubleDouble.FromComponents(estimate, correction);
// The result high is normal and finite over the entire positive input
// range. Rescale in the opposite direction to Sqrt and canonicalize any
// zero low, including underflow of an exceptionally sparse correction.
int inverseExponent = -(exponent / 2);
return DoubleDouble.FromComponents(Math.ScaleB(inverseRoot.High, inverseExponent),
Math.ScaleB(inverseRoot.Low, inverseExponent));
} }
// For finite, nonzero normalized significands and exponents bounded by the // For finite, nonzero normalized significands and exponents bounded by the
@@ -62,13 +62,13 @@ public readonly partial struct DoubleDouble
/// <summary>Adds one using double-double arithmetic, including its overflow and nonfinite behavior.</summary> /// <summary>Adds one using double-double arithmetic, including its overflow and nonfinite behavior.</summary>
public static DoubleDouble operator ++(DoubleDouble value) public static DoubleDouble operator ++(DoubleDouble value)
{ {
return value + One; return value + 1.0;
} }
/// <summary>Subtracts one using double-double arithmetic, including its overflow and nonfinite behavior.</summary> /// <summary>Subtracts one using double-double arithmetic, including its overflow and nonfinite behavior.</summary>
public static DoubleDouble operator --(DoubleDouble value) public static DoubleDouble operator --(DoubleDouble value)
{ {
return value - One; return value - 1.0;
} }
/// <summary>Tests normalization and canonical NaN and zero-residual bit patterns.</summary> /// <summary>Tests normalization and canonical NaN and zero-residual bit patterns.</summary>
@@ -0,0 +1,436 @@
namespace Just.PreciseMath;
public readonly partial struct DoubleDouble : IRootFunctions<DoubleDouble>
{
/// <summary>Returns the real cube root with double-double precision.</summary>
/// <remarks>
/// Preserves signed zero and signed infinity; NaN returns canonical NaN.
/// Scales to a bounded binade before two compensated Newton corrections.
/// Results are approximate, not guaranteed correctly rounded; finite nonzero
/// inputs are tested against a relative error bound of 2^-100.
/// </remarks>
[Pure]
public static DoubleDouble Cbrt(DoubleDouble value)
{
if (!IsFinite(value) || IsZero(value))
{
return value;
}
DoubleDouble magnitude = Abs(value);
int exponent = Math.ILogB(magnitude.High);
int remainder = ((exponent % 3) + 3) % 3;
exponent -= remainder;
// Scaled high is in [1, 8), including subnormal original inputs.
DoubleDouble scaled = new(Math.ScaleB(magnitude.High, -exponent));
DoubleDouble root = new(Math.Cbrt(scaled.High));
DoubleDouble square = root * root;
// First improve the binary64 seed (Math.Cbrt need not be correctly rounded).
// Re-anchor at the refined high, then derive its residual afresh; this
// also avoids retaining Newton iteration noise for exact binary cubes.
root = new DoubleDouble((root + ((scaled - (square * root)) / (square * 3.0))).High);
square = root * root;
root += (scaled - (square * root)) / (square * 3.0);
int rootExponent = exponent / 3;
root = FromComponents(Math.ScaleB(root.High, rootExponent), Math.ScaleB(root.Low, rootExponent));
root += RootLowCorrection(magnitude, root, 3);
return value.High < 0.0 ? -root : root;
}
/// <summary>Returns sqrt(x² + y²) without overflowing intermediate squares.</summary>
/// <remarks>
/// The result is nonnegative, including positive zero. Either infinity takes
/// precedence over NaN; otherwise NaN propagates. Uses scaled squares and an
/// exact allocating rescale at exponent boundaries, with exact squared-input
/// overflow classification. Approximate finite results
/// are tested against 2^-100 relative error plus one minimum binary64 subnormal;
/// correctly rounded finite components are not guaranteed.
/// </remarks>
[Pure]
public static DoubleDouble Hypot(DoubleDouble x, DoubleDouble y)
{
if (IsInfinity(x) || IsInfinity(y))
{
return PositiveInfinity;
}
if (IsNaN(x) || IsNaN(y))
{
return NaN;
}
DoubleDouble larger = Abs(x);
DoubleDouble smaller = Abs(y);
if (larger < smaller)
{
(larger, smaller) = (smaller, larger);
}
if (IsZero(smaller))
{
return larger;
}
int exponent = Math.ILogB(larger.High);
if (exponent - Math.ILogB(smaller.High) > 54)
{
// r=smaller/larger < 2^-54. sqrt(1+r²)=1+r²/2+O(r^4).
// Do not square r: its square can underflow even when the final
// low correction is representable (e.g. larger=2^1000, smaller=2^450).
// In the top binade this correction is <2^914, smaller than the
// minimum 2^917 gap between any canonical finite value and overflow.
return larger + ((smaller / larger) * (smaller * 0.5));
}
DoubleDouble a = FromComponents(Math.ScaleB(larger.High, -exponent), Math.ScaleB(larger.Low, -exponent));
DoubleDouble b = FromComponents(Math.ScaleB(smaller.High, -exponent), Math.ScaleB(smaller.Low, -exponent));
DoubleDouble root = Sqrt((a * a) + (b * b));
// root is finite and in [1, 3), with total exponent in [-1074, 1024].
// The boundary helper rounds the complete pair, not high before low.
if (exponent >= -969 && exponent <= 1021)
{
return FromComponents(Math.ScaleB(root.High, exponent), Math.ScaleB(root.Low, exponent));
}
if (exponent == 1023)
{
return HypotOverflowBoundary(larger, smaller, Zero, root);
}
return PreciseMathHelper.ScalePowerOfTwoBoundary(root, exponent);
}
/// <summary>Returns the three-dimensional Euclidean norm sqrt(x² + y² + z²).</summary>
/// <remarks>
/// Uses bounded scaled squares rather than unscaled products or nested norms.
/// Any infinity takes precedence over NaN; otherwise NaN propagates. Results
/// are nonnegative, including positive zero. A zero coordinate reduces to the
/// two-argument overload. Overflow classification uses exact squared inputs.
/// Approximate finite results are tested against 2^-100 relative error plus
/// one minimum binary64 subnormal, not guaranteed correctly rounded.
/// This convenience overload is not part of IRootFunctions.
/// </remarks>
[Pure]
public static DoubleDouble Hypot(DoubleDouble x, DoubleDouble y, DoubleDouble z)
{
if (IsInfinity(x) || IsInfinity(y) || IsInfinity(z))
{
return PositiveInfinity;
}
if (IsNaN(x) || IsNaN(y) || IsNaN(z))
{
return NaN;
}
DoubleDouble largest = Abs(x);
DoubleDouble middle = Abs(y);
DoubleDouble smallest = Abs(z);
// A fixed magnitude order gives the same evaluation for every permutation.
if (largest < middle)
{
(largest, middle) = (middle, largest);
}
if (middle < smallest)
{
(middle, smallest) = (smallest, middle);
}
if (largest < middle)
{
(largest, middle) = (middle, largest);
}
if (IsZero(smallest))
{
return Hypot(largest, middle);
}
int exponent = Math.ILogB(largest.High);
if (exponent - Math.ILogB(middle.High) > 54)
{
// For r=middle/largest and s=smallest/largest, use
// sqrt(1+r²+s²)=1+(r²+s²)/2+O((r²+s²)²). Avoid squaring
// tiny ratios and combine both corrections BEFORE halving: each
// half can underflow even when their sum is representable.
// In the top binade the total correction is <2^915, below the
// minimum 2^917 distance of a canonical finite input from overflow.
DoubleDouble correction = ((middle / largest) * middle) + ((smallest / largest) * smallest);
return largest + (correction * 0.5);
}
DoubleDouble a = FromComponents(Math.ScaleB(largest.High, -exponent), Math.ScaleB(largest.Low, -exponent));
DoubleDouble b = FromComponents(Math.ScaleB(middle.High, -exponent), Math.ScaleB(middle.Low, -exponent));
DoubleDouble c = FromComponents(Math.ScaleB(smallest.High, -exponent), Math.ScaleB(smallest.Low, -exponent));
// Sum the smaller squares first. All scaled highs are at most two,
// so the root stays in [1, 4) and cannot overflow before final rescaling.
DoubleDouble root = Sqrt((a * a) + ((b * b) + (c * c)));
if (exponent >= -969 && exponent <= 1021)
{
return FromComponents(Math.ScaleB(root.High, exponent), Math.ScaleB(root.Low, exponent));
}
if (exponent == 1023)
{
return HypotOverflowBoundary(largest, middle, smallest, root);
}
return PreciseMathHelper.ScalePowerOfTwoBoundary(root, exponent);
}
/// <summary>Returns the real nth root, or its reciprocal for negative n.</summary>
/// <remarks>
/// Supports every int degree, including int.MinValue. Degree zero and even
/// roots of negative nonzero values return canonical NaN. Odd degrees retain
/// the input sign; even degrees map either zero to positive zero (or positive
/// infinity for negative degrees). Negative degrees exchange zeros/infinities.
/// Degree one preserves both components; minus one uses the reciprocal kernel.
/// For |n| >= 2, finite nonzero results are tested against 2^-100 relative error,
/// not guaranteed correctly rounded. A binary64 seed is refined with scaled
/// integer powers, then an exponent-safe correction includes the input low.
/// </remarks>
[Pure]
public static DoubleDouble RootN(DoubleDouble value, int n)
{
if (n == 0 || IsNaN(value))
{
return NaN;
}
bool odd = (n & 1) != 0;
if (value.High < 0.0 && !odd)
{
return NaN;
}
if (IsZero(value) || IsInfinity(value))
{
bool infinite = IsZero(value) == (n < 0);
double result = infinite ? double.PositiveInfinity : 0.0;
return new DoubleDouble(odd ? Math.CopySign(result, value.High) : result);
}
switch (n)
{
case 1:
return value;
case -1:
return DDMath.Reciprocal(value);
case 2:
return Sqrt(value);
case -2:
return InvSqrt(value);
case 3:
return Cbrt(value);
case -3:
return 1.0 / Cbrt(value);
}
DoubleDouble magnitude = Abs(value);
// Widen before negation; |int.MinValue| is not representable as int.
long degree = n < 0 ? -(long)n : n;
int exponent = Math.ILogB(magnitude.High);
DoubleDouble root;
if (Math.ScaleB(magnitude.High, -exponent) == 1.0 && exponent % degree == 0)
{
// Preserve exact binary power roots without iterative roundoff.
root = new DoubleDouble(Math.ScaleB(1.0, (int)(exponent / degree)));
}
else
{
root = new DoubleDouble(Math.Exp(Math.Log(magnitude.High) / degree));
// r += r*(high/r^degree - 1)/degree. Track the power exponent
// separately: r^degree can cross a range boundary even though r
// and the Newton correction are finite. Three refinements also
// cover the degree-amplified seed error at the largest int degrees.
for (int iteration = 0; iteration < 3; ++iteration)
{
DoubleDouble residual = RootPowerRatio(magnitude.High, root, degree) - 1.0;
root += (root * residual) / (double)degree;
}
}
if (n < 0)
{
root = 1.0 / root;
}
root += RootLowCorrection(magnitude, root, n);
return value.High < 0.0 ? -root : root;
}
/// <summary>Returns the nonnegative square root with double-double precision.</summary>
/// <remarks>
/// Uses power-of-two scaling and an FMA-based Newton correction. Results are
/// approximate, not guaranteed correctly rounded; finite positive inputs are
/// tested against a relative error bound of 2^-100 across the binary64 range.
/// Signed zero and positive infinity are preserved. Negative nonzero values
/// (including negative infinity) and NaN return canonical NaN.
/// </remarks>
[Pure]
public static DoubleDouble Sqrt(DoubleDouble value)
{
if (double.IsNaN(value.High) || value.High < 0.0)
{
return DoubleDouble.NaN;
}
if (value.High == 0.0 || double.IsPositiveInfinity(value.High))
{
return value;
}
// Choose an even exponent, rounding negative odd exponents down too.
// For positive finite normalized inputs, exponent is in [-1074, 1022]
// and the scaled high is in [1, 4). Scaling the high is exact; any
// underflow in a very sparse low is far below the relative error bound.
int exponent = Math.ILogB(value.High) & ~1;
double high = Math.ScaleB(value.High, -exponent);
double low = Math.ScaleB(value.Low, -exponent);
double estimate = Math.Sqrt(high);
// Estimate is in [1, 2]. FMA avoids rounding its square before
// cancellation. One Newton correction reduces O(u) error to O(u^2),
// where u=2^-53; both components of the input contribute to the residual.
double residual = Math.FusedMultiplyAdd(-estimate, estimate, high) + low;
double correction = residual / (2.0 * estimate);
DoubleDouble root = DoubleDouble.FromComponents(estimate, correction);
// The root high stays normal, even if rounding reaches the next binade.
// Normalize again to canonicalize a zero low, including any underflow
// of an exceptionally sparse correction during rescaling.
int rootExponent = exponent / 2;
return DoubleDouble.FromComponents(Math.ScaleB(root.High, rootExponent), Math.ScaleB(root.Low, rootExponent));
}
/// <summary>Returns the reciprocal square root with double-double precision.</summary>
/// <remarks>
/// Uses power-of-two scaling and a compensated Newton step without double-double
/// division. Results are approximate, not guaranteed correctly rounded; positive
/// finite inputs are tested against a relative error bound of 2^-100 across the
/// binary64 range. Positive/negative zero maps to positive/negative infinity,
/// positive infinity to positive zero, and negative nonzero values or NaN to
/// canonical NaN.
/// </remarks>
[Pure]
public static DoubleDouble InvSqrt(DoubleDouble value)
{
if (double.IsNaN(value.High) || value.High < 0.0)
{
return DoubleDouble.NaN;
}
if (value.High == 0.0)
{
return new DoubleDouble(Math.CopySign(double.PositiveInfinity, value.High));
}
if (double.IsPositiveInfinity(value.High))
{
return DoubleDouble.Zero;
}
// As in Sqrt, the even exponent is in [-1074, 1022], high is in [1, 4),
// and any underflow in a sparse scaled low is below the error bound.
int exponent = Math.ILogB(value.High) & ~1;
double high = Math.ScaleB(value.High, -exponent);
double low = Math.ScaleB(value.Low, -exponent);
double estimate = 1.0 / Math.Sqrt(high);
// With u=2^-53, the seed has O(u) relative error. Preserve the square's
// FMA residual before cancellation in 1 - (high + low)*estimate^2:
// using only the rounded square would leave O(u) error after refinement.
// All leading products are bounded and normal. Omitting low*squareError
// contributes only O(u^2), as does one Newton step's remaining error.
double square = estimate * estimate;
double squareError = Math.FusedMultiplyAdd(estimate, estimate, -square);
double residual = Math.FusedMultiplyAdd(-high, square, 1.0);
residual = Math.FusedMultiplyAdd(-high, squareError, residual);
residual = Math.FusedMultiplyAdd(-low, square, residual);
double correction = (0.5 * estimate) * residual;
DoubleDouble inverseRoot = DoubleDouble.FromComponents(estimate, correction);
// The result high is normal and finite over the entire positive input
// range. Rescale in the opposite direction to Sqrt and canonicalize any
// zero low, including underflow of an exceptionally sparse correction.
int inverseExponent = -(exponent / 2);
return DoubleDouble.FromComponents(Math.ScaleB(inverseRoot.High, inverseExponent),
Math.ScaleB(inverseRoot.Low, inverseExponent));
}
// Inputs are nonnegative finite and the largest high has exponent 1023.
// The two-dimensional overload supplies zero for z.
// The scaled root is approximate: it can round to the overflow midpoint
// from below. Classify x²+y²+z² against T² exactly before rescaling; T is the
// binary64 overflow midpoint 2^1024-2^970. A midpoint itself overflows.
[MethodImpl(MethodImplOptions.NoInlining)]
private static DoubleDouble HypotOverflowBoundary(DoubleDouble x, DoubleDouble y, DoubleDouble z, DoubleDouble scaledRoot)
{
BigInteger a = PreciseMathHelper.ArithmeticUnits(x);
BigInteger b = PreciseMathHelper.ArithmeticUnits(y);
BigInteger c = PreciseMathHelper.ArithmeticUnits(z);
BigInteger threshold = PreciseMathHelper.ArithmeticUnits(double.MaxValue)
+ PreciseMathHelper.ArithmeticUnits(Math.ScaleB(1.0, 970));
if ((a * a) + (b * b) + (c * c) >= threshold * threshold)
{
return PositiveInfinity;
}
DoubleDouble result = PreciseMathHelper.ScalePowerOfTwoBoundary(scaledRoot, 1023);
// If only the approximation overflowed, clamp to the largest canonical
// finite pair. Its distance below T is 2^917 (<2^-106 relative), within
// the approximate error budget; do not claim correctly rounded lows.
return IsInfinity(result)
? new DoubleDouble(double.MaxValue, Math.BitDecrement(Math.ScaleB(1.0, 970)))
: result;
}
// high is positive finite, degree is in [4, 2^31], and root is a nearby
// positive nth-root estimate. The resulting ratio is near one. Intermediate
// mantissas stay in [1, 4] before normalization, with wide tracked exponents.
private static DoubleDouble RootPowerRatio(double high, DoubleDouble root, long degree)
{
long factorExponent = Math.ILogB(root.High);
DoubleDouble factor = FromComponents(Math.ScaleB(root.High, -(int)factorExponent),
Math.ScaleB(root.Low, -(int)factorExponent));
DoubleDouble power = One;
long powerExponent = 0;
while (degree != 0)
{
if ((degree & 1) != 0)
{
power *= factor;
powerExponent += factorExponent;
power = RootNormalizeMantissa(power, ref powerExponent);
}
degree >>= 1;
if (degree != 0)
{
factor *= factor;
factorExponent *= 2;
factor = RootNormalizeMantissa(factor, ref factorExponent);
}
}
// The seed/refinements keep powerExponent close to ILogB(high), so
// scaling high into the power's binade is bounded and normal.
return new DoubleDouble(Math.ScaleB(high, -(int)powerExponent)) / power;
}
private static DoubleDouble RootNormalizeMantissa(DoubleDouble value, ref long exponent)
{
int adjustment = Math.ILogB(value.High);
exponent += adjustment;
return FromComponents(Math.ScaleB(value.High, -adjustment), Math.ScaleB(value.Low, -adjustment));
}
// value is positive finite, root approximates value.High^(1/n), |n| >= 2.
// Apply low*root/(n*high) using bounded mantissas and separate exponents.
// Forming low/high first can underflow before root rescales the correction.
// Normalization bounds |low/high| by 2^-53: omitted quadratic terms are
// below 2^-106 relative, within the tested 2^-100 root error budget. Since
// these roots are normal and bounded by 2^537, the correction cannot overflow.
private static DoubleDouble RootLowCorrection(DoubleDouble value, DoubleDouble root, int n)
{
if (value.Low == 0.0)
{
return Zero;
}
int highExponent = Math.ILogB(value.High);
int lowExponent = Math.ILogB(Math.Abs(value.Low));
int rootExponent = Math.ILogB(root.High);
double high = Math.ScaleB(value.High, -highExponent);
double low = Math.ScaleB(value.Low, -lowExponent);
DoubleDouble scaledRoot = FromComponents(Math.ScaleB(root.High, -rootExponent), Math.ScaleB(root.Low, -rootExponent));
DoubleDouble correction = (scaledRoot * low) / (new DoubleDouble(high) * n);
int exponent = lowExponent - highExponent + rootExponent;
int resultExponent = Math.ILogB(Math.Abs(correction.High)) + exponent;
if (resultExponent < -1075)
{
return Zero;
}
if (resultExponent < -969)
{
return PreciseMathHelper.ScalePowerOfTwoBoundary(correction, exponent);
}
return FromComponents(Math.ScaleB(correction.High, exponent), Math.ScaleB(correction.Low, exponent));
}
}
@@ -0,0 +1,120 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleCbrtTests
{
[Fact]
public void ExactCubesAndSpecialValues()
{
foreach (double value in new[] { 0.0, -0.0, double.NaN, double.PositiveInfinity, double.NegativeInfinity })
{
DoubleDouble result = DoubleDouble.Cbrt(new DoubleDouble(value));
AssertBits(new DoubleDouble(Math.Cbrt(value)), result);
}
foreach (double root in new[] { -3.0, -2.0, -1.0, 0.5, 1.0, 2.0, 3.0 })
{
AssertBits(new DoubleDouble(root), DoubleDouble.Cbrt(new DoubleDouble(root * root * root)));
}
for (int exponent = -1074; exponent <= 1023; exponent += 3)
{
DoubleDouble input = new(Math.ScaleB(1.0, exponent));
AssertBits(new DoubleDouble(Math.ScaleB(1.0, exponent / 3)), DoubleDouble.Cbrt(input));
}
}
[Fact]
public void CubesMeetExactBoundAcrossEveryInputExponent()
{
Random random = new(271828);
for (int exponent = -1074; exponent <= 1023; ++exponent)
{
double high = Math.ScaleB(1.0 + random.NextDouble(), exponent);
double dense = Math.ScaleB(random.NextDouble(), exponent - 53);
foreach (double low in new[] { 0.0, dense, -dense, double.Epsilon, -double.Epsilon })
{
DoubleDouble input = DoubleDouble.FromComponents(high, low);
if (input.High > 0.0 && DoubleDouble.IsFinite(input))
{
AssertBound(input, DoubleDouble.Cbrt(input));
AssertBound(-input, DoubleDouble.Cbrt(-input));
AssertBits(DoubleDouble.Cbrt(input), DDMath.Cbrt(input));
}
}
}
foreach (double low in new[] { 0.0, Math.BitDecrement(Math.ScaleB(1.0, 970)), -Math.ScaleB(1.0, 970) })
{
DoubleDouble input = DoubleDouble.FromComponents(double.MaxValue, low);
AssertBound(input, DoubleDouble.Cbrt(input));
}
}
[Fact]
public void SparseNearOneCorrectionsSurvive()
{
// cbrt(1+d) = 1+d/3+O(d^2); for these d, the quadratic is
// below half an ulp of the rounded binary64 correction d/3.
foreach (int exponent in new[] { -100, -500, -1000, -1072 })
{
foreach (double sign in new[] { -1.0, 1.0 })
{
double low = Math.ScaleB(sign, exponent);
DoubleDouble result = DoubleDouble.Cbrt(DoubleDouble.FromComponents(1.0, low));
result.High.ShouldBe(1.0);
result.Low.ShouldBe(low / 3.0);
}
}
}
[Fact]
public void ScalingDoesNotEraseARepresentableSparseCorrection()
{
// cbrt(2^900 + d) = 2^300 + d/(3*2^600) + O(d²/2^1500).
// Scaling d=2^-174 by 2^-900 leaves epsilon, whose division by 3
// underflows, although the final correction 2^-774/3 is representable.
foreach (double sign in new[] { -1.0, 1.0 })
{
DoubleDouble input = DoubleDouble.FromComponents(Math.ScaleB(1.0, 900), Math.ScaleB(sign, -174));
DoubleDouble result = DoubleDouble.Cbrt(input);
AssertBits(DoubleDouble.FromComponents(Math.ScaleB(1.0, 300), Math.ScaleB(sign / 3.0, -774)), result);
AssertBits(-result, DoubleDouble.Cbrt(-input));
}
}
private static void AssertBound(DoubleDouble input, DoubleDouble actual)
{
DoubleDouble.IsFinite(actual).ShouldBeTrue();
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
Math.Sign(actual.High).ShouldBe(Math.Sign(input.High));
// Exact dyadic oracle, independent of the implementation's arithmetic:
// x*(1-t)^3 <= |y|^3 <= x*(1+t)^3, t=2^-100, x=|input|.
BigInteger x = BigInteger.Abs(Units(input.High) + Units(input.Low)) << 2148;
BigInteger y = BigInteger.Abs(Units(actual.High) + Units(actual.Low));
BigInteger scale = BigInteger.One << 100;
BigInteger cube = BigInteger.Pow(y, 3) << 300;
(cube >= x * BigInteger.Pow(scale - 1, 3)
&& cube <= x * BigInteger.Pow(scale + 1, 3)).ShouldBeTrue(
$"Cbrt bound failed for ({input.High:R}, {input.Low:R}): ({actual.High:R}, {actual.Low:R})");
}
private static BigInteger Units(double value)
{
long bits = BitConverter.DoubleToInt64Bits(value);
int exponent = (int)((bits >> 52) & 0x7ff);
BigInteger significand = bits & 0xfffffffffffffL;
if (exponent != 0)
{
significand += BigInteger.One << 52;
significand <<= exponent - 1;
}
return bits < 0 ? -significand : significand;
}
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));
}
}
@@ -0,0 +1,349 @@
using System.Numerics;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleHypotTests
{
[Fact]
public void SpecialValuesAndZeroOperandsFollowTheContract()
{
double[] values = [0.0, -0.0, 1.0, -1.0, double.PositiveInfinity, double.NegativeInfinity, double.NaN];
foreach (double x in values)
{
foreach (double y in values)
{
if (!double.IsFinite(x) || !double.IsFinite(y) || x == 0.0 || y == 0.0)
{
AssertBits(new DoubleDouble(double.Hypot(x, y)), DoubleDouble.Hypot(new DoubleDouble(x), new DoubleDouble(y)));
}
}
}
DoubleDouble sparse = DoubleDouble.FromComponents(double.MaxValue, double.Epsilon);
AssertBits(sparse, DoubleDouble.Hypot(-sparse, DoubleDouble.NegativeZero));
AssertBits(sparse, DoubleDouble.Hypot(DoubleDouble.Zero, -sparse));
AssertBits(DoubleDouble.PositiveInfinity, DoubleDouble.Hypot(new DoubleDouble(double.MaxValue), new DoubleDouble(double.MaxValue)));
}
[Fact]
public void ScaledPythagoreanTriplesAreExact()
{
for (int exponent = -1074; exponent <= 1021; ++exponent)
{
DoubleDouble x = new(Math.ScaleB(3.0, exponent));
DoubleDouble y = new(Math.ScaleB(4.0, exponent));
DoubleDouble expected = new(Math.ScaleB(5.0, exponent));
AssertBits(expected, DoubleDouble.Hypot(x, y));
AssertBits(expected, DoubleDouble.Hypot(-y, -x));
}
}
[Fact]
public void FiniteResultsMeetExactSquaredBoundAcrossTheRange()
{
Random random = new(123456);
for (int exponent = -1074; exponent <= 1023; ++exponent)
{
double high = Math.ScaleB(1.0 + (random.NextDouble() * 0.1), exponent);
foreach (int gap in new[] { 0, 1, 26, 54, 55, 500, 1100 })
{
double second = Math.ScaleB(1.0, exponent - gap);
foreach (double sign in new[] { -1.0, 1.0 })
{
DoubleDouble x = DoubleDouble.FromComponents(high, Math.ScaleB(sign, exponent - 54));
DoubleDouble y = DoubleDouble.FromComponents(second, Math.ScaleB(-sign, exponent - gap - 54));
DoubleDouble result = DoubleDouble.Hypot(x, y);
AssertBound(x, y, result);
AssertBits(result, DoubleDouble.Hypot(-y, x));
AssertBits(result, DDMath.Hypot(x, y));
}
}
}
DoubleDouble maximum = new(double.MaxValue);
AssertBound(maximum, new DoubleDouble(Math.ScaleB(1.0, 995)), DoubleDouble.Hypot(maximum, new DoubleDouble(Math.ScaleB(1.0, 995))));
}
[Fact]
public void WidelySeparatedOperandsRetainRepresentableCorrections()
{
// sqrt(a^2+b^2) = a+b^2/(2a)+O(b^4/a^3). These exact dyadics
// put the omitted term below half an ulp of the expected low.
foreach ((int large, int small) in new[] { (0, -100), (1000, 450), (-500, -600) })
{
DoubleDouble result = DoubleDouble.Hypot(new DoubleDouble(Math.ScaleB(1.0, large)), new DoubleDouble(Math.ScaleB(1.0, small)));
AssertBits(DoubleDouble.FromComponents(Math.ScaleB(1.0, large), Math.ScaleB(1.0, (2 * small) - large - 1)), result);
}
}
[Fact]
public void OverflowClassificationUsesTheExactInputSquares()
{
DoubleDouble x = DoubleDouble.FromComponents(double.MaxValue, Math.BitDecrement(Math.ScaleB(1.0, 970)));
double boundaryY = Math.ScaleB(1.0, 971);
// T=2^1024-2^970 is the binary64 overflow midpoint. For the lower y,
// T²-x²-y² = 3*2^1888-5*2^1834 > 0, though the scaled root can round
// up to T before rescaling. The adjacent boundaryY is above the threshold.
foreach (double y in new[] { Math.BitDecrement(boundaryY), boundaryY, Math.BitIncrement(boundaryY) })
{
AssertOverflowBoundary(x, new DoubleDouble(y));
}
}
[Fact]
public void ExactOverflowMidpointAndAdjacentLowsAreClassified()
{
// 55²+48²=73² and 73 divides 2^54-1, so these scaled integer legs
// have hypotenuse exactly T=(2^54-1)*2^970. Split integer components
// before scaling; no DD arithmetic supplies the expected threshold.
const long factor = ((1L << 54) - 1) / 73;
const long first = 55 * factor;
const long second = 48 * factor;
DoubleDouble x = DoubleDouble.FromComponents(Math.ScaleB((double)first, 970), Math.ScaleB(first - (long)(double)first, 970));
DoubleDouble y = DoubleDouble.FromComponents(Math.ScaleB((double)second, 970), Math.ScaleB(second - (long)(double)second, 970));
BigInteger threshold = Units(double.MaxValue) + Units(Math.ScaleB(1.0, 970));
BigInteger a = Units(x.High) + Units(x.Low);
BigInteger b = Units(y.High) + Units(y.Low);
((a * a) + (b * b)).ShouldBe(threshold * threshold);
foreach (double low in new[] { Math.BitDecrement(x.Low), x.Low, Math.BitIncrement(x.Low) })
{
AssertOverflowBoundary(DoubleDouble.FromComponents(x.High, low), y);
}
}
[Fact]
public void ThreeDimensionalSpecialValuesAndZeroReductionFollowTheContract()
{
double[] values = [0.0, -0.0, 1.0, -1.0, double.PositiveInfinity, double.NegativeInfinity, double.NaN];
foreach (double x in values)
{
foreach (double y in values)
{
foreach (double z in values)
{
DoubleDouble result = DoubleDouble.Hypot(new DoubleDouble(x), new DoubleDouble(y), new DoubleDouble(z));
if (double.IsInfinity(x) || double.IsInfinity(y) || double.IsInfinity(z))
{
AssertBits(DoubleDouble.PositiveInfinity, result);
}
else if (double.IsNaN(x) || double.IsNaN(y) || double.IsNaN(z))
{
AssertBits(DoubleDouble.NaN, result);
}
else if (x == 0.0 && y == 0.0 && z == 0.0)
{
AssertBits(DoubleDouble.Zero, result);
}
else
{
AssertBound(new DoubleDouble(x), new DoubleDouble(y), new DoubleDouble(z), result);
}
AssertBits(result, DDMath.Hypot(new DoubleDouble(x), new DoubleDouble(y), new DoubleDouble(z)));
}
}
}
DoubleDouble[] finite = [new(double.Epsilon), new(double.MaxValue), new(2.0),
DoubleDouble.FromComponents(1.0, double.Epsilon)];
foreach (DoubleDouble x in finite)
{
foreach (DoubleDouble y in finite)
{
DoubleDouble expected = DoubleDouble.Hypot(x, y);
AssertBits(expected, DoubleDouble.Hypot(x, y, DoubleDouble.NegativeZero));
AssertBits(expected, DoubleDouble.Hypot(x, DoubleDouble.Zero, y));
AssertBits(expected, DoubleDouble.Hypot(DoubleDouble.NegativeZero, x, y));
}
}
}
[Fact]
public void ThreeDimensionalExactBinaryNormsCoverTheExponentRange()
{
// 1²+2²+2²=3²; all scaled inputs and outputs here are exact dyadics.
for (int exponent = -1074; exponent <= 1021; ++exponent)
{
DoubleDouble x = new(Math.ScaleB(1.0, exponent));
DoubleDouble y = new(Math.ScaleB(2.0, exponent));
DoubleDouble expected = new(Math.ScaleB(3.0, exponent));
AssertBits(expected, DoubleDouble.Hypot(x, y, y));
AssertBits(expected, DoubleDouble.Hypot(-y, x, -y));
}
AssertBits(new DoubleDouble(7.0), DoubleDouble.Hypot(new DoubleDouble(2.0), new DoubleDouble(3.0), new DoubleDouble(6.0)));
AssertBits(new DoubleDouble(13.0), DoubleDouble.Hypot(new DoubleDouble(3.0), new DoubleDouble(4.0), new DoubleDouble(12.0)));
}
[Fact]
public void ThreeDimensionalFiniteResultsMeetExactSquaredBound()
{
Random random = new(314265);
for (int exponent = -1074; exponent <= 1023; ++exponent)
{
double high = Math.ScaleB(1.0 + (random.NextDouble() * 0.1), exponent);
foreach (int gap in new[] { 0, 1, 26, 54, 55, 500, 1100 })
{
foreach (double sign in new[] { -1.0, 1.0 })
{
DoubleDouble x = DoubleDouble.FromComponents(high, Math.ScaleB(sign, exponent - 54));
DoubleDouble y = DoubleDouble.FromComponents(Math.ScaleB(1.0, exponent - gap), Math.ScaleB(-sign, exponent - gap - 54));
DoubleDouble z = DoubleDouble.FromComponents(Math.ScaleB(0.75, exponent - gap), Math.ScaleB(sign, exponent - gap - 55));
DoubleDouble result = DoubleDouble.Hypot(x, y, z);
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));
}
}
}
}
[Fact]
public void ThreeDimensionalSparseCorrectionsAreCombinedBeforeFinalRounding()
{
// sqrt(a²+2b²) = a+b²/a+O(b^4/a^3). The omitted term is below
// half an ulp of the expected low in these exact dyadic cases.
foreach ((int large, int small) in new[] { (0, -100), (1000, 450), (-500, -600), (0, -537), (-500, -787) })
{
DoubleDouble x = new(Math.ScaleB(1.0, large));
DoubleDouble y = new(Math.ScaleB(1.0, small));
DoubleDouble expected = DoubleDouble.FromComponents(x.High, Math.ScaleB(1.0, (2 * small) - large));
AssertThreeDimensionalPermutations(x, y, y, expected);
}
DoubleDouble sparse = DoubleDouble.FromComponents(Math.ScaleB(1.0, 1000), double.Epsilon);
AssertThreeDimensionalPermutations(sparse, DoubleDouble.One, DoubleDouble.One,
DoubleDouble.FromComponents(sparse.High, Math.ScaleB(1.0, -1000)));
}
[Fact]
public void ThreeDimensionalOverflowUsesAllExactInputSquares()
{
// The third coordinate changes a finite two-coordinate norm to overflow.
DoubleDouble value = new(Math.ScaleB(1.25, 1023));
DoubleDouble.IsFinite(DoubleDouble.Hypot(value, value)).ShouldBeTrue();
AssertThreeDimensionalOverflowBoundary(value, value, value);
AssertThreeDimensionalOverflowBoundary(new DoubleDouble(double.MaxValue), value, DoubleDouble.One);
DoubleDouble x = DoubleDouble.FromComponents(double.MaxValue, Math.BitDecrement(Math.ScaleB(1.0, 970)));
double boundaryY = Math.ScaleB(1.0, 971);
foreach (double y in new[] { Math.BitDecrement(boundaryY), boundaryY, Math.BitIncrement(boundaryY) })
{
AssertThreeDimensionalOverflowBoundary(x, new DoubleDouble(y), DoubleDouble.One);
}
// 1²+2²+2²=3², and 3 divides 2^54-1. These exact scaled integers
// give norm T=(2^54-1)*2^970. Even an epsilon low on the first
// coordinate decides which side of the exact overflow midpoint we are on.
const long factor = ((1L << 54) - 1) / 3;
DoubleDouble first = new(Math.ScaleB((double)factor, 970));
DoubleDouble second = new(Math.ScaleB((double)(2 * factor), 970));
BigInteger a = Units(first.High);
BigInteger b = Units(second.High);
BigInteger threshold = Units(double.MaxValue) + Units(Math.ScaleB(1.0, 970));
((a * a) + (2 * b * b)).ShouldBe(threshold * threshold);
foreach (double low in new[] { -double.Epsilon, 0.0, double.Epsilon })
{
AssertThreeDimensionalOverflowBoundary(DoubleDouble.FromComponents(first.High, low), second, second);
}
}
private static void AssertThreeDimensionalOverflowBoundary(DoubleDouble x, DoubleDouble y, DoubleDouble z)
{
BigInteger a = Units(x.High) + Units(x.Low);
BigInteger b = Units(y.High) + Units(y.Low);
BigInteger c = Units(z.High) + Units(z.Low);
BigInteger threshold = Units(double.MaxValue) + Units(Math.ScaleB(1.0, 970));
bool overflow = ((a * a) + (b * b) + (c * c)) >= threshold * threshold;
DoubleDouble result = DoubleDouble.Hypot(x, y, z);
DoubleDouble.IsPositiveInfinity(result).ShouldBe(overflow);
if (!overflow)
{
AssertBound(x, y, z, result);
}
AssertThreeDimensionalPermutations(x, y, z, result);
}
private static void AssertThreeDimensionalPermutations(DoubleDouble x, DoubleDouble y, DoubleDouble z, DoubleDouble expected)
{
foreach (DoubleDouble a in new[] { x, -x })
{
foreach (DoubleDouble b in new[] { y, -y })
{
foreach (DoubleDouble c in new[] { z, -z })
{
AssertBits(expected, DoubleDouble.Hypot(a, b, c));
AssertBits(expected, DoubleDouble.Hypot(a, c, b));
AssertBits(expected, DoubleDouble.Hypot(b, a, c));
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));
}
}
}
}
private static void AssertOverflowBoundary(DoubleDouble x, DoubleDouble y)
{
BigInteger a = Units(x.High) + Units(x.Low);
BigInteger b = Units(y.High) + Units(y.Low);
BigInteger threshold = Units(double.MaxValue) + Units(Math.ScaleB(1.0, 970));
bool overflow = ((a * a) + (b * b)) >= threshold * threshold;
foreach (DoubleDouble first in new[] { x, -x })
{
foreach (DoubleDouble second in new[] { y, -y })
{
DoubleDouble result = DoubleDouble.Hypot(first, second);
DoubleDouble.IsPositiveInfinity(result).ShouldBe(overflow);
if (!overflow)
{
AssertBound(first, second, result);
}
AssertBits(result, DoubleDouble.Hypot(second, first));
AssertBits(result, DDMath.Hypot(first, second));
}
}
}
private static void AssertBound(DoubleDouble x, DoubleDouble y, DoubleDouble actual)
{
AssertBound(x, y, DoubleDouble.Zero, actual);
}
private static void AssertBound(DoubleDouble x, DoubleDouble y, DoubleDouble z, DoubleDouble actual)
{
DoubleDouble.IsFinite(actual).ShouldBeTrue();
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
double.IsNegative(actual.High).ShouldBeFalse();
// Exact integer units of 2^-1074. The squared inequalities are equivalent
// to |actual/sqrt(x*x+y*y+z*z)-1| <= 2^-100, allowing one subnormal unit.
BigInteger a = Units(x.High) + Units(x.Low);
BigInteger b = Units(y.High) + Units(y.Low);
BigInteger c = Units(z.High) + Units(z.Low);
BigInteger r = Units(actual.High) + Units(actual.Low);
BigInteger sum = (a * a) + (b * b) + (c * c);
BigInteger scale = BigInteger.One << 100;
BigInteger lower = BigInteger.Max(BigInteger.Zero, r - 1) * scale;
BigInteger upper = (r + 1) * scale;
((lower * lower <= sum * (scale + 1) * (scale + 1))
&& (upper * upper >= sum * (scale - 1) * (scale - 1))).ShouldBeTrue(
$"Hypot bound failed for ({x.High:R}, {x.Low:R}), ({y.High:R}, {y.Low:R}), ({z.High:R}, {z.Low:R}): ({actual.High:R}, {actual.Low:R})");
}
private static BigInteger Units(double value)
{
long bits = BitConverter.DoubleToInt64Bits(value);
int exponent = (int)((bits >> 52) & 0x7ff);
BigInteger significand = bits & 0xfffffffffffffL;
if (exponent != 0)
{
significand += BigInteger.One << 52;
significand <<= exponent - 1;
}
return bits < 0 ? -significand : significand;
}
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));
}
}
@@ -0,0 +1,27 @@
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleRootFunctionsTests
{
[Fact]
public void ExistingRootsAreAvailableOnTheTypeAndMatchFacadeBits()
{
DoubleDouble[] values = [DoubleDouble.Zero, DoubleDouble.NegativeZero,
DoubleDouble.NaN, DoubleDouble.PositiveInfinity, DoubleDouble.NegativeInfinity,
new(-1.0), new(double.Epsilon), new(2.0), new(double.MaxValue),
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));
}
}
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));
}
}
@@ -0,0 +1,243 @@
using System.Globalization;
using System.Numerics;
using Just.PreciseMath.Tests.ReferenceData;
using Shouldly;
using Xunit;
namespace Just.PreciseMath.Tests;
public class DoubleDoubleRootNTests
{
public static IEnumerable<TheoryDataRow<double, double, int, string>> ReferenceCases => RootNReferenceData.Cases();
[Theory]
[MemberData(nameof(ReferenceCases))]
public void LargeDegreesMeetIndependentHighPrecisionReferences(double high, double low, int degree, string reference)
{
DoubleDouble input = DoubleDouble.FromComponents(high, low);
(Units(input.High) + Units(input.Low)).ShouldBe(Units(high) + Units(low));
DoubleDouble actual = DoubleDouble.RootN(input, degree);
DoubleDouble.IsFinite(actual).ShouldBeTrue();
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
Math.Sign(actual.High).ShouldBe(Math.Sign(high));
string[] parts = reference.Split('e');
int point = parts[0].IndexOf('.', StringComparison.Ordinal);
int decimals = point < 0 ? 0 : parts[0].Length - point - 1;
BigInteger numerator = BigInteger.Parse(parts[0].Replace(".", "", StringComparison.Ordinal), CultureInfo.InvariantCulture);
int exponent = int.Parse(parts[1], CultureInfo.InvariantCulture) - decimals;
BigInteger denominator = BigInteger.One;
if (exponent >= 0)
{
numerator *= BigInteger.Pow(10, exponent);
}
else
{
denominator = BigInteger.Pow(10, -exponent);
}
BigInteger actualUnits = Units(actual.High) + Units(actual.Low);
BigInteger error = BigInteger.Abs((actualUnits * denominator) - (numerator << 1074));
// 2^-100 relative plus the separately bounded 2^-350 relative reference
// rounding allowance. For |degree|>=2 every nonzero root is normal.
BigInteger magnitude = BigInteger.Abs(numerator);
BigInteger bound = (magnitude << 1324) + (magnitude << 1074);
((error << 350) <= bound).ShouldBeTrue(
$"RootN reference failed for ({high:R}, {low:R}), {degree}: ({actual.High:R}, {actual.Low:R})");
}
[Fact]
public void SpecialValuesMatchRuntimeRootNSemantics()
{
double[] values = [0.0, -0.0, 1.0, -1.0, double.PositiveInfinity, double.NegativeInfinity, double.NaN];
int[] degrees = [int.MinValue, int.MinValue + 1, -1000, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 1000, int.MaxValue];
foreach (double value in values)
{
foreach (int degree in degrees)
{
AssertBits(new DoubleDouble(double.RootN(value, degree)), DoubleDouble.RootN(new DoubleDouble(value), degree));
}
}
foreach (int degree in new[] { int.MinValue, -4, -2, 0, 2, 4, int.MaxValue - 1 })
{
DoubleDouble.IsNaN(DoubleDouble.RootN(new DoubleDouble(-2.0), degree)).ShouldBeTrue();
}
}
[Fact]
public void IdentitiesAndExistingKernelsKeepTheirComponentBits()
{
DoubleDouble[] values = [new(double.Epsilon), new(double.MaxValue), new(2.0),
DoubleDouble.FromComponents(1.0, double.Epsilon), DoubleDouble.FromComponents(1.0, -Math.ScaleB(1.0, -54))];
foreach (DoubleDouble value in values)
{
AssertBits(value, DoubleDouble.RootN(value, 1));
AssertBits(-value, DoubleDouble.RootN(-value, 1));
AssertBits(DDMath.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));
AssertBits(1.0 / DoubleDouble.Cbrt(value), DoubleDouble.RootN(value, -3));
}
}
[Fact]
public void SmallDegreesMeetIndependentExactPowerInequalities()
{
Random random = new(8675309);
for (int exponent = -1074; exponent <= 1023; ++exponent)
{
double high = Math.ScaleB(1.0 + random.NextDouble(), exponent);
foreach (int degree in new[] { -5, -4, -3, -2, 2, 3, 4, 5 })
{
DoubleDouble input = DoubleDouble.FromComponents(high, Math.ScaleB((degree < 0 ? -1.0 : 1.0), exponent - 54));
AssertBound(input, DoubleDouble.RootN(input, degree), degree);
if ((degree & 1) != 0)
{
AssertBits(-DoubleDouble.RootN(input, degree), DoubleDouble.RootN(-input, degree));
}
}
}
foreach (double high in new[] { double.Epsilon, 1e-308, 0.5, 1.0, 2.0, 81.0, 1e308, double.MaxValue })
{
foreach (int degree in new[] { -31, -17, -7, -6, 6, 7, 17, 31 })
{
DoubleDouble input = new(high);
AssertBound(input, DoubleDouble.RootN(input, degree), degree);
}
}
}
[Fact]
public void ExactPowerOfTwoRootsAndSparseCorrectionsSurvive()
{
foreach (int degree in new[] { -31, -7, -5, -4, 4, 5, 7, 31 })
{
for (int exponent = -1074; exponent <= 1023; ++exponent)
{
if (exponent % degree == 0)
{
AssertBits(new DoubleDouble(Math.ScaleB(1.0, exponent / degree)),
DoubleDouble.RootN(new DoubleDouble(Math.ScaleB(1.0, exponent)), degree));
}
}
}
foreach (int degree in new[] { int.MinValue, int.MinValue + 1, -7, -4, 4, 7, int.MaxValue })
{
foreach (int exponent in new[] { -100, -500, -1000, -1070 })
{
foreach (double sign in new[] { -1.0, 1.0 })
{
double low = Math.ScaleB(sign, exponent);
// (1+d)^(1/n) = 1+d/n+O(d²); the quadratic term is
// below half an ulp of this rounded correction.
AssertBits(DoubleDouble.FromComponents(1.0, low / degree),
DoubleDouble.RootN(DoubleDouble.FromComponents(1.0, low), degree));
}
}
}
foreach (double sign in new[] { -1.0, 1.0 })
{
DoubleDouble input = DoubleDouble.FromComponents(Math.ScaleB(1.0, 900), Math.ScaleB(sign, -174));
// d*r/(n*high), with exact r=2^225 and n=4, is sign*2^-851.
AssertBits(DoubleDouble.FromComponents(Math.ScaleB(1.0, 225), Math.ScaleB(sign, -851)), DoubleDouble.RootN(input, 4));
}
}
[Fact]
public void NegativeDegreeRetainsSparseCorrectionAfterLargeRescaling()
{
// (2^-900+d)^(-1/4) = 2^225 - d*2^225/(4*2^-900) + O(d²).
// For d=±2^-1074, the correction is exactly ∓2^49 at binary64
// residual precision; the quadratic contribution is below half an ulp.
foreach (double sign in new[] { -1.0, 1.0 })
{
DoubleDouble input = DoubleDouble.FromComponents(Math.ScaleB(1.0, -900), sign * double.Epsilon);
AssertBits(DoubleDouble.FromComponents(Math.ScaleB(1.0, 225), Math.ScaleB(-sign, 49)), DoubleDouble.RootN(input, -4));
}
}
[Fact]
public void GenericContractAndFacadeDispatchToTypeMembers()
{
DoubleDouble[] values = [DoubleDouble.Zero, DoubleDouble.NegativeZero, DoubleDouble.NaN,
DoubleDouble.PositiveInfinity, DoubleDouble.NegativeInfinity, new(-2.0), new(double.Epsilon),
new(double.MaxValue), DoubleDouble.FromComponents(1.0, Math.ScaleB(1.0, -54))];
foreach (DoubleDouble value in values)
{
AssertBits(DoubleDouble.Sqrt(value), GenericSqrt(value));
AssertBits(DoubleDouble.Cbrt(value), GenericCbrt(value));
AssertBits(DoubleDouble.Hypot(value, DoubleDouble.One), GenericHypot(value, DoubleDouble.One));
foreach (int degree in new[] { int.MinValue, -7, -3, -2, -1, 0, 1, 2, 3, 4, 7, int.MaxValue })
{
DoubleDouble result = DoubleDouble.RootN(value, degree);
AssertBits(result, GenericRootN(value, degree));
AssertBits(result, DDMath.RootN(value, degree));
}
}
}
private static T GenericSqrt<T>(T value) where T : IRootFunctions<T>
{
return T.Sqrt(value);
}
private static T GenericCbrt<T>(T value) where T : IRootFunctions<T>
{
return T.Cbrt(value);
}
private static T GenericHypot<T>(T x, T y) where T : IRootFunctions<T>
{
return T.Hypot(x, y);
}
private static T GenericRootN<T>(T value, int n) where T : IRootFunctions<T>
{
return T.RootN(value, n);
}
private static void AssertBound(DoubleDouble input, DoubleDouble actual, int degree)
{
DoubleDouble.IsFinite(actual).ShouldBeTrue();
DoubleDouble.IsCanonical(actual).ShouldBeTrue();
(actual.High > 0.0).ShouldBeTrue();
int n = Math.Abs(degree);
BigInteger x = Units(input.High) + Units(input.Low);
BigInteger y = Units(actual.High) + Units(actual.Low);
BigInteger scale = BigInteger.One << 100;
BigInteger power = BigInteger.Pow(y, n) << (100 * n);
BigInteger target;
if (degree > 0)
{
target = x << (1074 * (n - 1));
}
else
{
power *= x;
target = BigInteger.One << (1074 * (n + 1));
}
// Positive degree: x*(1-t)^n <= y^n <= x*(1+t)^n.
// Negative degree: (1-t)^n <= x*y^n <= (1+t)^n.
(power >= target * BigInteger.Pow(scale - 1, n)
&& power <= target * BigInteger.Pow(scale + 1, n)).ShouldBeTrue(
$"RootN bound failed for ({input.High:R}, {input.Low:R}), {degree}: ({actual.High:R}, {actual.Low:R})");
}
private static BigInteger Units(double value)
{
long bits = BitConverter.DoubleToInt64Bits(value);
int exponent = (int)((bits >> 52) & 0x7ff);
BigInteger significand = bits & 0xfffffffffffffL;
if (exponent != 0)
{
significand += BigInteger.One << 52;
significand <<= exponent - 1;
}
return bits < 0 ? -significand : significand;
}
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));
}
}
@@ -0,0 +1,552 @@
// 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
{
internal static IEnumerable<TheoryDataRow<double, double, int, string>> Cases()
{
yield return new(5e-324, 0.0, -2147483648, "1.00000034665698229989419923796875777142350075077854142640012630161140035250946208062185210474013999887450772416095548287e+0");
yield return new(5e-324, 0.0, -2147483647, "1.00000034665698246131896392462575136026763137841817002802990415631982348939017868346053058592181983751719306827754166287e+0");
yield return new(5e-324, 0.0, -1000000000, "1.00000074444034901696036381644003423525905486873887287323824029337273291571015728203130009836888843502733609521183992801e+0");
yield return new(5e-324, 0.0, -65537, "1.01142384002835288586286148750623054509548208019438032340563103479272537894981668040512149540131911093711836321338905673e+0");
yield return new(5e-324, 0.0, -127, "3.51332423149811114020420972638491067314578593704937010495960733201345230188073045551931737928462302543556060659003148773e+2");
yield return new(5e-324, 0.0, -4, "6.70739427389146133442979443040089115095100886804424598010444624697399335178119519563972910651814833367532781502518894456e+80");
yield return new(5e-324, 0.0, 4, "1.49089193085383538923363765955953714632683137184618392935846325784949277701877750660443120422125365510292025771153525999e-81");
yield return new(5e-324, 0.0, 127, "2.84630718404714827561921128805165683120547576725897388984985662361606740551824176452756624111279429201687409288809745411e-3");
yield return new(5e-324, 0.0, 65537, "9.88705190073399253268372770807075564446601108407927473285629397102400555343754225610683684043240475748153422665512860764e-1");
yield return new(5e-324, 0.0, 1000000000, "9.99999255560205174060318520460459025903574964191594743242974034903643759902333796233914558824581283331575333534453596699e-1");
yield return new(5e-324, 0.0, 2147483646, "9.99999653343137548278214219760787616188995075297095288449907228992257872125380304629649124329672415108890653015495698989e-1");
yield return new(5e-324, 0.0, 2147483647, "9.99999653343137709702867138770758688026024754510221793463292812563739593899022538200764508771816592500417149346007889706e-1");
yield return new(1.5e-323, 0.0, -2147483648, "1.00000034614540091008856398784588832934833939940384321821509192422238098397565536039203174108037788806133223389548491563e+0");
yield return new(1.5e-323, 0.0, -2147483647, "1.00000034614540107127510494333143441288758471913357302345902565352196837788167165466174428113228547134613258294156626160e+0");
yield return new(1.5e-323, 0.0, -1000000000, "1.00000074334173591104441344407164987101742829979226698697162253301985187388526450926051853195346190199442614533446902462e+0");
yield return new(1.5e-323, 0.0, -65537, "1.01140688543253041372103223187090116727472103542266225642050743466868456750846473458880554365112511858539629177285826333e+0");
yield return new(1.5e-323, 0.0, -127, "3.48306332827533373443119127669145245901198674876056141874527614089272699787533548759389498627941559001060103414095066068e+2");
yield return new(1.5e-323, 0.0, -4, "5.09651752703788425957703665020926495608911454305921480449451222216463991937051260397794976885539652420438032469828508406e+80");
yield return new(1.5e-323, 0.0, 4, "1.96212412631729705033078154058910173357105507332164681146669468771984961762045773719486689548007756898757149986001752693e-81");
yield return new(1.5e-323, 0.0, 127, "2.87103594092605225026177329476949823881912320702218188422824388198637964426870678987366041312774553778143040578914351038e-3");
yield return new(1.5e-323, 0.0, 65537, "9.88721764112123644791674837505276028791293390686917665393482307041020229197385292630303551980854129993869743716089530119e-1");
yield return new(1.5e-323, 0.0, 1000000000, "9.99999256658816645481195473888974733905771895733114097627438716007644506468932982122581474976899813407205758619840425093e-1");
yield return new(1.5e-323, 0.0, 2147483646, "9.99999653854718584135674368772976285495222654238342476353728283741528505633179340443855404823254001618072042874733382982e-1");
yield return new(1.5e-323, 0.0, 2147483647, "9.99999653854718745322103886473288706299633470528815449108118280195980171720321616584313570188097200437253589935748570860e-1");
yield return new(2.2250738585072014e-308, 0.0, -2147483648, "1.00000032987284258596031552046040790830599004270199566048050787753143852523144289420893543631852366392988521790302868175e+0");
yield return new(2.2250738585072014e-308, 0.0, -2147483647, "1.00000032987284273956935345414973306343177972736116949915708809079535577689366845591060612936945038792964065167023503939e+0");
yield return new(2.2250738585072014e-308, 0.0, -1000000000, "1.00000070839666944506624946706274822650818826658295909649049750542985227975497853111730600357578840179294134794432761409e+0");
yield return new(2.2250738585072014e-308, 0.0, -65537, "1.01086773603532536932331686782943863353085513221167653957750733090842307517354590260390892360515413223706473125346105030e+0");
yield return new(2.2250738585072014e-308, 0.0, -127, "2.64522034845304669747621535741342655777939331006265807634757459073560114047656102617319188703058316015983014706750957576e+2");
yield return new(2.2250738585072014e-308, 0.0, -4, "8.18773715074641276175512015429796283075074324712432370618218536007567547824852929155240369448016154013101539920067009834e+76");
yield return new(2.2250738585072014e-308, 0.0, 4, "1.22133866975546195086019597071117283027094025981639387493045310083030448293378253341035004249805099426031227511728968499e-77");
yield return new(2.2250738585072014e-308, 0.0, 127, "3.78040340036250950702951053691902736360880275129299568249051702347989433785368892472981094503080381972466248075064997203e-3");
yield return new(2.2250738585072014e-308, 0.0, 65537, "9.89249101887503915866461491810556032286036656752873859925040445338527948016932432229660076996017338303459819956738289004e-1");
yield return new(2.2250738585072014e-308, 0.0, 1000000000, "9.99999291603832380419539892619828616382751750227774031050960754752929713544793305606028160552179366557225828202793914033e-1");
yield return new(2.2250738585072014e-308, 0.0, 2147483646, "9.99999670127265922878191434717557873045485282713747668829617111085491819774836659158522557155007471897958277879135166201e-1");
yield return new(2.2250738585072014e-308, 0.0, 2147483647, "9.99999670127266076487128168616471501977721679308186186576508026987264268412404385026183530565121896323124177674585847034e-1");
yield return new(2.225073858507201e-308, 0.0, -2147483648, "1.00000032987284258596031562385801858548696023895983632610360738082749311080125300707121255031888223719325938530486528935e+0");
yield return new(2.225073858507201e-308, 0.0, -2147483647, "1.00000032987284273956935355754734378876103032132259793811022091957089714252022637322029488672893180704626668920803740192e+0");
yield return new(2.225073858507201e-308, 0.0, -1000000000, "1.00000070839666944506624968910751044719811814096296516166110552526513042082095105458821777934200752035311916938314202439e+0");
yield return new(2.225073858507201e-308, 0.0, -65537, "1.01086773603532536932674176868055212180050657224952509547259712474176514057434424709090347320373355178747041730279693014e+0");
yield return new(2.225073858507201e-308, 0.0, -127, "2.64522034845304670210107289451508381257978670850588518861590447195519702378317194530066904902734199762887330121944915070e+2");
yield return new(2.225073858507201e-308, 0.0, -4, "8.18773715074641321626583537117020913511494541443896563298960534639533131600118144026836217696910989194252502981227226227e+76");
yield return new(2.225073858507201e-308, 0.0, 4, "1.22133866975546188306228036933245893141686363606809552923034822452809083249276724715142720468056663605153578269552119074e-77");
yield return new(2.225073858507201e-308, 0.0, 127, "3.78040340036250950041991857242502362407959531536015656154300652485833325055355261364874258892781579394213854338860091398e-3");
yield return new(2.225073858507201e-308, 0.0, 65537, "9.89249101887503915863109836623393774711320735920855410037584420122449942670641163899890493376565559218270542941585193518e-1");
yield return new(2.225073858507201e-308, 0.0, 1000000000, "9.99999291603832380419539670575380986898588509615126250561101716368405228405442527424581721175087930252054286017115588231e-1");
yield return new(2.225073858507201e-308, 0.0, 2147483646, "9.99999670127265922878191331320015315661828707851369965953859576763217516443237533860827507688586064629691809818241452178e-1");
yield return new(2.225073858507201e-308, 0.0, 2147483647, "9.99999670127266076487128065218928992742282015802799386153593420299115046457964607345697750203997776272764966310281895126e-1");
yield return new(1e-308, 0.0, -2147483648, "1.00000033024527400103758415561171827491536112569422922280293403635443021066023550387443074530186544783958352991173805943e+0");
yield return new(1e-308, 0.0, -2147483647, "1.00000033024527415482004903880163282640997264185745141815648807956600084415882765872208722665376951655176037444148049266e+0");
yield return new(1e-308, 0.0, -1000000000, "1.00000070919646012185669645712321274057427259564244859621779091990197530076194341878032270767698095200397040432580684656e+0");
yield return new(1e-308, 0.0, -65537, "1.01088007237893177201822429377414246386552631376767894483656333882829889424379576641116080655681927313604127676038433880e+0");
yield return new(1e-308, 0.0, -127, "2.66193134612613073925290487110242339291144660910469944694682427579093437393117975739543391926830739650693909104628871467e+2");
yield return new(1e-308, 0.0, -4, "1.00000000000000002266834366568788578576688918082216786934503169822053290495945819636764875306200829619210785845479431326e+77");
yield return new(1e-308, 0.0, 4, "9.99999999999999977331656334312114728086915364909915495978338849867251827386069354804974783593763012673903308351645866322e-78");
yield return new(1e-308, 0.0, 127, "3.75667089031159721107064567254577115566506792595422232685256005454633117192975656077437348073699081750912181687839359360e-3");
yield return new(1e-308, 0.0, 65537, "9.89237029518914741706722637984633793238233438732724096447807882487109254856799437585398286103849380300419544683032575765e-1");
yield return new(1e-308, 0.0, 1000000000, "9.99999290804042837405655986706458090614540941309878365415124935746420726074619716211744748068707252260526645781054135847e-1");
yield return new(1e-308, 0.0, 2147483646, "9.99999669754834753302671920112307536738477011911734917314987683265242095109622698360447132830437620583229350963739681148e-1");
yield return new(1e-308, 0.0, 2147483647, "9.99999669754834907085035374708973065368561880135462995625068565408894995273139669283295926764392716904525636823326991849e-1");
yield return new(0.5, 0.0, -2147483648, "1.00000000032277180844536493248552273843658307496786927891303194319273282055207279274560899910825827041716398431879961122e+0");
yield return new(0.5, 0.0, -2147483647, "1.00000000032277180859566726840708505646978792137378497109441088492007257435200513095235613984898693606233800546941310214e+0");
yield return new(0.5, 0.0, -1000000000, "1.00000000069314718080017181643183694246616754030093671697092943849725637366367267953869962182007700078613681030647808347e+0");
yield return new(0.5, 0.0, -65537, "1.00001057648116457167215270349394297497241259861435957360758872150558426213383522387510316657634694682655547830200490957e+0");
yield return new(0.5, 0.0, -127, "1.00547277302221306857687621252345877639796444631811870580077371310514545554465530820363514238115898312615622360304681075e+0");
yield return new(0.5, 0.0, -4, "1.18920711500272106671749997056047591529297209246381741301900222471946666822691715987078134453813767371603739477476921319e+0");
yield return new(0.5, 0.0, 4, "8.40896415253714543031125476233214895040034262356784510813226085974924754953902239814324004199292536172801573743519085080e-1");
yield return new(0.5, 0.0, 127, "9.94557015198170698569268684444940799852443429879734200797346759717920936017114553597195565202843484576595299109473455393e-1");
yield return new(0.5, 0.0, 65537, "9.99989423630696199059051957856192099125312797258828893503844008959857591057955446665637936075276031946365194145573704209e-1");
yield return new(0.5, 0.0, 1000000000, "9.99999999306852819680281197486364482220171244240985209083889726852474576362142530175522871575565518796328395279731427276e-1");
yield return new(0.5, 0.0, 2147483646, "9.99999999677228191358212036018890550795819617840102092160738848623174608504927461571971108701774630323720801581835599653e-1");
yield return new(0.5, 0.0, 2147483647, "9.99999999677228191508514371983406113774081566473621756422724422138637696671801252012588344215482041058766213312547646733e-1");
yield return new(0.5, 2.7755575615628914e-17, -2147483648, "1.00000000032277180844536490663612858781101325817974082185309632348018361768750986133347797701181155732144460680605452344e+0");
yield return new(0.5, 2.7755575615628914e-17, -2147483647, "1.00000000032277180859566724255769089380715593878979026195433102694670753151335954426649391557465115244325529018052775645e+0");
yield return new(0.5, 2.7755575615628914e-17, -1000000000, "1.00000000069314718080017176092068567273094408094496206532703287653683823769363207836703406521596291999900764262651713314e+0");
yield return new(0.5, 2.7755575615628914e-17, -65537, "1.00001057648116457167130567451269721690340741584755599519957031131539451518903592934904538371249763038005869288655923483e+0");
yield return new(0.5, 2.7755575615628914e-17, -127, "1.00547277302221306813738840809709465781017926157000688843565680683269853903869377087334049514564231859793930893066832692e+0");
yield return new(0.5, 2.7755575615628914e-17, -4, "1.18920711500272105021393596900950952810190083893950374621265391372214412900741121440935135307633400819301897172662137216e+0");
yield return new(0.5, 2.7755575615628914e-17, 4, "8.40896415253714554700907495476096914756896438875198116898633081836766669500743544854403361781590350585643334655789770195e-1");
yield return new(0.5, 2.7755575615628914e-17, 127, "9.94557015198170699003985258293555297767116497252732858137251872732246247911937753245268758378189048112051080177486954855e-1");
yield return new(0.5, 2.7755575615628914e-17, 65537, "9.99989423630696199059898968920549972152051701286602220185092235618534950722376882850490513066299597833870621896256344076e-1");
yield return new(0.5, 2.7755575615628914e-17, 1000000000, "9.99999999306852819680281252997515675000598772430766262071671605787830397881300389414121428904361431529850572970119102532e-1");
yield return new(0.5, 2.7755575615628914e-17, 2147483646, "9.99999999677228191358212061868284708808602359998747355618761863159715432869072576038054552026413929509730952726888673918e-1");
yield return new(0.5, 2.7755575615628914e-17, 2147483647, "9.99999999677228191508514397832800259749802147166910333967366807596395483332596922930674459225086523595731432812046289110e-1");
yield return new(0.5, -2.7755575615628914e-17, -2147483648, "1.00000000032277180844536495833491688906215432668562633593165677740447644016246211581971454553843474112263254679006748566e+0");
yield return new(0.5, -2.7755575615628914e-17, -2147483647, "1.00000000032277180859566729425647921913242133888740894838435884524038597790086193573316697481572226315366168558506822296e+0");
yield return new(0.5, -2.7755575615628914e-17, -1000000000, "1.00000000069314718080017187194298821220139408114482760560476223855516006643237142899683874750408955647721113418734162753e+0");
yield return new(0.5, -2.7755575615628914e-17, -65537, "1.00001057648116457167299973247518873308843805270686164767890629514118603441612772142650785642178245087193751649571677487e+0");
yield return new(0.5, -2.7755575615628914e-17, -127, "1.00547277302221306901636401694982291957432182715352481414357394544659235835779529370559036788205252430596396422431517617e+0");
yield return new(0.5, -2.7755575615628914e-17, -4, "1.18920711500272108322106397211144344764883977703601883441142019480364855737336686868211243635542630661445072063660127670e+0");
yield return new(0.5, -2.7755575615628914e-17, 4, "8.40896415253714531361343456990332389470896206334640001405691357837800501523300005629911375081494779148609572890079440457e-1");
yield return new(0.5, -2.7755575615628914e-17, 127, "9.94557015198170698134552110596326277996165624874984511108857673876214065063057832534352157309682172091285906468850430219e-1");
yield return new(0.5, -2.7755575615628914e-17, 65537, "9.99989423630696199058204946791834226051556051378268025354431167097825641544310787333230730082454883377220606198773446755e-1");
yield return new(0.5, -2.7755575615628914e-17, 1000000000, "9.99999999306852819680281141975213289439740634563298353931308554525718932454936455083652304610119722775823419402487806294e-1");
yield return new(0.5, -2.7755575615628914e-17, 2147483646, "9.99999999677228191358212010169496392783035440751829155053689032543046673895129581512703075336315064345712555141586081522e-1");
yield return new(0.5, -2.7755575615628914e-17, 2147483647, "9.99999999677228191508514346134011967798359550850706173420232637990417996919032077230979681311981084900735954125366889879e-1");
yield return new(0.9999999999999999, 0.0, -2147483648, "1.00000000000000000000000005169878828456423254932229952798060705801423260788874332891627451330292496725726238449108659070e+0");
yield return new(0.9999999999999999, 0.0, -2147483647, "1.00000000000000000000000005169878830863835686537313674738146839754785222125091768697741458132003422046618565892619246806e+0");
yield return new(0.9999999999999999, 0.0, -1000000000, "1.00000000000000000000000011102230246251566020533899501121303922012868601342443752764310067064249540219773634901592531998e+0");
yield return new(0.9999999999999999, 0.0, -65537, "1.00000000000000000000169404004550888292423261710120077616732308551762555599393870812292695130494658208659574050178268555e+0");
yield return new(0.9999999999999999, 0.0, -127, "1.00000000000000000087419135797256425396745163406071560529396578496558600048437765775874919413265016476612535931325583645e+0");
yield return new(0.9999999999999999, 0.0, -4, "1.00000000000000002775557561562891543652073608950652123436054811668055287427735824907041019199855906528841129944234488063e+0");
yield return new(0.9999999999999999, 0.0, 4, "9.99999999999999972244424384371085333851241665387905516501351867559804764645872199864814836954450440009765264637937162988e-1");
yield return new(0.9999999999999999, 0.0, 127, "9.99999999999999999125808642027435746796758896293200373874847557445465207384084617901203457495941398006831367457017801847e-1");
yield return new(0.9999999999999999, 0.0, 65537, "9.99999999999999999998305959954491117075770252670475011570805902091226051158840550019015793833234248249514927929010668837e-1");
yield return new(0.9999999999999999, 0.0, 1000000000, "9.99999999999999999999999888977697537484339794661017314738604858182142000365962220201830254817045130707198801983600921438e-1");
yield return new(0.9999999999999999, 0.0, 2147483646, "9.99999999999999999999999948301211667287518796155253245463849473718935340871699646039859828534214211334955016376523563115e-1");
yield return new(0.9999999999999999, 0.0, 2147483647, "9.99999999999999999999999948301211691361643134626865925383244183854203538924947887514943921858653250787526091990804466704e-1");
yield return new(0.9999999999999999, 2.7755575615628914e-17, -2147483648, "1.00000000000000000000000003877409121342317387389311409468393878988401191878794502936114737397107141562213991710314073927e+0");
yield return new(0.9999999999999999, 2.7755575615628914e-17, -2147483647, "1.00000000000000000000000003877409123147876711093124175866289287334624275055488398913386985563497393433861585068077463056e+0");
yield return new(0.9999999999999999, 2.7755575615628914e-17, -1000000000, "1.00000000000000000000000008326672684688674399844627847051015127434424745031679614468738149059612642431044815895399270944e+0");
yield return new(0.9999999999999999, 2.7755575615628914e-17, -65537, "1.00000000000000000000127053003413166217554204756767150853381061267641897489566588272134823503886039045120177383140169046e+0");
yield return new(0.9999999999999999, 2.7755575615628914e-17, -127, "1.00000000000000000065564351847942318130506236129854477957942524865848328607826014888915994902441728715619625002213241632e+0");
yield return new(0.9999999999999999, 2.7755575615628914e-17, -4, "1.00000000000000002081668171172168621627868749452311585771931691272848671244510406686390342876661333891339512398504134642e+0");
yield return new(0.9999999999999999, 2.7755575615628914e-17, 4, "9.99999999999999979183318288278314217055549992604987162199306413524152589650406949752763407970454235149364714064939189764e-1");
yield return new(0.9999999999999999, 2.7755575615628914e-17, 127, "9.99999999999999999344356481520576819124806062025532946771786728105112843794049476348163653830771894814358618550365186961e-1");
yield return new(0.9999999999999999, 2.7755575615628914e-17, 65537, "9.99999999999999999998729469965868337824459566678896122068842138510889913498731321854977859024133235747584883754210448644e-1");
yield return new(0.9999999999999999, 2.7755575615628914e-17, 1000000000, "9.99999999999999999999999916733273153113256001553728462837648519705400868381177075354596971516878440574995062876985386500e-1");
yield return new(0.9999999999999999, 2.7755575615628914e-17, 2147483646, "9.99999999999999999999999961225908750465639635215050485399403608915831369892240163661385866165358860128818205994311876344e-1");
yield return new(0.9999999999999999, 2.7755575615628914e-17, 2147483647, "9.99999999999999999999999961225908768521232889068759744767257953692371885974806300261535463780572696300548847801911609676e-1");
yield return new(0.9999999999999999, -2.7755575615628914e-17, -2147483648, "1.00000000000000000000000006462348535570529158348389199547831951797706676371112415830425482668121128322778524836501837307e+0");
yield return new(0.9999999999999999, -2.7755575615628914e-17, -2147483647, "1.00000000000000000000000006462348538579794697854743893734888272770741319919731843865740918478496698280106407903972987119e+0");
yield return new(0.9999999999999999, -2.7755575615628914e-17, -1000000000, "1.00000000000000000000000013877787807814457718260369007718241720014034531885657417317916072252106705516384030642055967375e+0");
yield return new(0.9999999999999999, -2.7755575615628914e-17, -65537, "1.00000000000000000000211755005688610368467813014021609394951573430700879037106641315419422932425288431516356384150866965e+0");
yield return new(0.9999999999999999, -2.7755575615628914e-17, -127, "1.00000000000000000109273919746570533274352514965421494845884415895304969015464550105918086883591529276268897116895794846e+0");
yield return new(0.9999999999999999, -2.7755575615628914e-17, -4, "1.00000000000000003469446951953614489750402773289446838020499921816153445830849383322606736191348093899283980963616710333e+0");
yield return new(0.9999999999999999, -2.7755575615628914e-17, 4, "9.99999999999999965305530480463856306202187509128106827575595659667294814160264312910834456950799265466292438529698261378e-1");
yield return new(0.9999999999999999, -2.7755575615628914e-17, 127, "9.99999999999999998907260802534294668450553804023778802134775378404831104587602850234443792095480959181879863465238130175e-1");
yield return new(0.9999999999999999, -2.7755575615628914e-17, 65537, "9.99999999999999999997882449943113896315326353878027324391435265339238903114376197276468969576661105934478183469158346712e-1");
yield return new(0.9999999999999999, -2.7755575615628914e-17, 1000000000, "9.99999999999999999999999861222121921855422817396329182117026672220858005632943872080175758775580250549559652312009604340e-1");
yield return new(0.9999999999999999, -2.7755575615628914e-17, 2147483646, "9.99999999999999999999999935376514584109397598363048971327251146689463082081983548591682573236383260053943813493012633875e-1");
yield return new(0.9999999999999999, -2.7755575615628914e-17, 2147483647, "9.99999999999999999999999935376514614202053021452565238845980680733414838769788455538761271237560453828293935087646132075e-1");
yield return new(1.0, 0.0, -2147483648, "1e+0");
yield return new(1.0, 0.0, -2147483647, "1e+0");
yield return new(1.0, 0.0, -1000000000, "1e+0");
yield return new(1.0, 0.0, -65537, "1e+0");
yield return new(1.0, 0.0, -127, "1e+0");
yield return new(1.0, 0.0, -4, "1e+0");
yield return new(1.0, 0.0, 4, "1e+0");
yield return new(1.0, 0.0, 127, "1e+0");
yield return new(1.0, 0.0, 65537, "1e+0");
yield return new(1.0, 0.0, 1000000000, "1e+0");
yield return new(1.0, 0.0, 2147483646, "1e+0");
yield return new(1.0, 0.0, 2147483647, "1e+0");
yield return new(1.0, 5.551115123125783e-17, -2147483648, "9.99999999999999999999999974150605857717885877733292441215563349010503192784050421523101748409009792828148646874365746308e-1");
yield return new(1.0, 5.551115123125783e-17, -2147483647, "9.99999999999999999999999974150605845680823719707874833801900363995536168967790568669524385492969317548891225265329401946e-1");
yield return new(1.0, 5.551115123125783e-17, -1000000000, "9.99999999999999999999999944488848768742174519562373645991565310590469017415380332501748775391912490863929007719609814295e-1");
yield return new(1.0, 5.551115123125783e-17, -65537, "9.99999999999999999999152979977245558608413352482315899757711013246529789986600098938025002561354385545954630560535512239e-1");
yield return new(1.0, 5.551115123125783e-17, -127, "9.99999999999999999562904321013717909698379639957606487779382801961188642669047031644878526758473317676198347234838468513e-1");
yield return new(1.0, 5.551115123125783e-17, -4, "9.99999999999999986122212192185543726187090245673689312009159954765387699501138476653069422955384053318892662051077327470e-1");
yield return new(1.0, 5.551115123125783e-17, 4, "1.00000000000000001387778780781445646640590419304988530252979393494530670007038413358483346021892600523138234453858528890e+0");
yield return new(1.0, 5.551115123125783e-17, 127, "1.00000000000000000043709567898628209049267299263087247519616579139459107467152726831620701461594728115118439449792976232e+0");
yield return new(1.0, 5.551115123125783e-17, 65537, "1.00000000000000000000084702002275444139158736496060304717670175540856273680601756673261119674511887530222275503948985548e+0");
yield return new(1.0, 5.551115123125783e-17, 1000000000, "1.00000000000000000000000005551115123125782548043762943549634570898672481520788190286931216167044010733032272534010970087e+0");
yield return new(1.0, 5.551115123125783e-17, 2147483646, "1.00000000000000000000000002584939416635623844952793117206220838512295079424246269654068003680023125544290437696694380613e+0");
yield return new(1.0, 5.551115123125783e-17, 2147483647, "1.00000000000000000000000002584939415431917628029212583438927778135486649489155156828359745402601637369017578652983912227e+0");
yield return new(1.0, -5.551115123125783e-17, -2147483648, "1.00000000000000000000000002584939414228211555719633569558829479993750163122639565648758805254974376634423409044788917587e+0");
yield return new(1.0, -5.551115123125783e-17, -2147483647, "1.00000000000000000000000002584939415431917771522175397119313624145367244891148975108219859027333814510068234697336638872e+0");
yield return new(1.0, -5.551115123125783e-17, -1000000000, "1.00000000000000000000000005551115123125782856192554045507371059855160400803778524822618220000393459151955893977352169308e+0");
yield return new(1.0, -5.551115123125783e-17, -65537, "1.00000000000000000000084702002275444143860642153962829039644289079920310478146759583967149271494366022243383341061634097e+0");
yield return new(1.0, -5.551115123125783e-17, -127, "1.00000000000000000043709567898628211475635733136770213060143948344979322519992763694345728511612994204128309412121062027e+0");
yield return new(1.0, -5.551115123125783e-17, -4, "1.00000000000000001387778780781445723677788194794423721597981133770007980754814574896657241795401820836751522338875318351e+0");
yield return new(1.0, -5.551115123125783e-17, 4, "9.99999999999999986122212192185542955815112490779358780676823289575783716315114073181954608761587402888244162174341102749e-1");
yield return new(1.0, -5.551115123125783e-17, 127, "9.99999999999999999562904321013717885434695301220776853585212271403974135887231559501635883249058602244949766477461167207e-1");
yield return new(1.0, -5.551115123125783e-17, 65537, "9.99999999999999999999152979977245558561394295903290656538049530020636338670815050991212352262856212449560334887503782716e-1");
yield return new(1.0, -5.551115123125783e-17, 1000000000, "9.99999999999999999999999944488848768742171438074462626414200421025931938468368788187584725726213397233697633756061537187e-1");
yield return new(1.0, -5.551115123125783e-17, 2147483646, "9.99999999999999999999999974150605833643760115542440691133933465929044961310553349881008430235163076133152452881397638226e-1");
yield return new(1.0, -5.551115123125783e-17, 2147483647, "9.99999999999999999999999974150605845680822284778246696998041903896804399070935223647126897860312815433847341387841556752e-1");
yield return new(1.0000000000000002, 0.0, -2147483648, "9.99999999999999999999999896602423430871552120510938585683809197817796501341773422210950500925234448960931091631367152196e-1");
yield return new(1.0000000000000002, 0.0, -2147483647, "9.99999999999999999999999896602423382723303488409272165176227996764409328058220190119293994106278144819615015297565274062e-1");
yield return new(1.0000000000000002, 0.0, -1000000000, "9.99999999999999999999999777955395074968716567176979190354495558380264504278101408431678491828675435467101366607943945476e-1");
yield return new(1.0000000000000002, 0.0, -65537, "9.99999999999999999996611919908982234715772054060928687752933478525665052269131234304068512531772597237971289640856668445e-1");
yield return new(1.0000000000000002, 0.0, -127, "9.99999999999999998251617284054871785521940387782250404299819593767295655839058800338033578936544976250130965803942228815e-1");
yield return new(1.0000000000000002, 0.0, -4, "9.99999999999999944488848768742180682538194144401113671850926086030581158705255201036377218311832424967234855407533190091e-1");
yield return new(1.0000000000000002, 0.0, 4, "1.00000000000000005551115123125782239894971687517556698994799844790777614917174816728505435194256556320612401332195152613e+0");
yield return new(1.0000000000000002, 0.0, 127, "1.00000000000000000174838271594512821753490173363341249424248202397360589222520642161522897792640480671001183768136126031e+0");
yield return new(1.0000000000000002, 0.0, 65537, "1.00000000000000000000338808009101776528423942502577446319575916800188937706716603853840851067821355321571737055145057498e+0");
yield return new(1.0000000000000002, 0.0, 1000000000, "1.00000000000000000000000022204460492503128343282307011345208075484662608450469113456937767720903206517091711606882748880e+0");
yield return new(1.0000000000000002, 0.0, 2147483646, "1.00000000000000000000000010339757666542494518853395586742727773871528108291422341908166862080056495471814967844671461545e+0");
yield return new(1.0000000000000002, 0.0, 2147483647, "1.00000000000000000000000010339757661727669651159073852588262232884025287473726589495475293974058812524921874103013195654e+0");
yield return new(1.0000000000000002, 5.551115123125783e-17, -2147483648, "9.99999999999999999999999870753029288589443737962743574113212050708073776443154972950713038130718144779920614676724807059e-1");
yield return new(1.0000000000000002, 5.551115123125783e-17, -2147483647, "9.99999999999999999999999870753029228404132947835662218956681690643792913587159583907836454833668502278800125689205257775e-1");
yield return new(1.0000000000000002, 5.551115123125783e-17, -1000000000, "9.99999999999999999999999722444243843710903412691009240603743366704975905688597690797298781560034441104530226977170069435e-1");
yield return new(1.0000000000000002, 5.551115123125783e-17, -65537, "9.99999999999999999995764899886227793512264502631021295924426109220279291823554411085539182829205752013759585592203860287e-1");
yield return new(1.0000000000000002, 5.551115123125783e-17, -127, "9.99999999999999997814521605068589793039267913041064108499846210831337214634527106210476798063484155306940544589323753469e-1");
yield return new(1.0000000000000002, 5.551115123125783e-17, -4, "9.99999999999999930611060960927728260585173164545306351696429722942558484716133550333701537805399207075064342290074972420e-1");
yield return new(1.0000000000000002, 5.551115123125783e-17, 4, "1.00000000000000006938893903907227655423968780354298990589338064189461003432090781932948898575249437951197900703508609133e+0");
yield return new(1.0000000000000002, 5.551115123125783e-17, 127, "1.00000000000000000218547839493141021173704790167090890533371054816409733288337041408013210127051952945716487015713676337e+0");
yield return new(1.0000000000000002, 5.551115123125783e-17, 65537, "1.00000000000000000000423510011377220648775343344195237743191049891968804622457359756358678095013113823784283462634180221e+0");
yield return new(1.0000000000000002, 5.551115123125783e-17, 1000000000, "1.00000000000000000000000027755575615628909658730906779659403212270776421574836814656648747177689897961183980775464253894e+0");
yield return new(1.0000000000000002, 5.551115123125783e-17, 2147483646, "1.00000000000000000000000012924697083178117789834337449227564661995771827186237510668137446559010299299488788554997834588e+0");
yield return new(1.0000000000000002, 5.551115123125783e-17, 2147483647, "1.00000000000000000000000012924697077159586705216435448582277194311256447674225195224248977314536444011253623652468309491e+0");
yield return new(1.0000000000000002, -5.551115123125783e-17, -2147483648, "9.99999999999999999999999922451817573153661937988761734057627257110625964814117295999237149676637610680873994583293108197e-1");
yield return new(1.0000000000000002, -5.551115123125783e-17, -2147483647, "9.99999999999999999999999922451817537042475463912510916390173671569039538479232754199832005509560895242809562738896136363e-1");
yield return new(1.0000000000000002, -5.551115123125783e-17, -1000000000, "9.99999999999999999999999833466546306226532803150863241169155203664006241307698970732868247483377846027769554273677193292e-1");
yield return new(1.0000000000000002, -5.551115123125783e-17, -65537, "9.99999999999999999997458939931736675966299379512780248854461152382846594393487723332029059923744435052309951865516568611e-1");
yield return new(1.0000000000000002, -5.551115123125783e-17, -127, "9.99999999999999998688712963041153802459349833848734415639557131498716369453374178021399731351231982908874946575140505322e-1");
yield return new(1.0000000000000002, -5.551115123125783e-17, -4, "9.99999999999999958366636576556634067456187317874366422346577146541356891031745075161046964544193083451504209722911848173e-1");
yield return new(1.0000000000000002, -5.551115123125783e-17, 4, "1.00000000000000004163336342344336766588076263063761266944488122280776235580662391889682154827506549228271883212444644033e+0");
yield return new(1.0000000000000002, -5.551115123125783e-17, 127, "1.00000000000000000131128703695884619926012385944757606024045175522145114905246463473188545795060611480476161127714711828e+0");
yield return new(1.0000000000000002, -5.551115123125783e-17, 65537, "1.00000000000000000000254106006826332403370707747349027355453689603026389694899868824894032928146668733104735629789770799e+0");
yield return new(1.0000000000000002, -5.551115123125783e-17, 1000000000, "1.00000000000000000000000016653345369377346719684916449222204397252765922735563493884903375928778364519987838622597996855e+0");
yield return new(1.0000000000000002, -5.551115123125783e-17, 2147483646, "1.00000000000000000000000007754818249906871104379490910577568794528944099846347708044594172872606009909654547785972907199e+0");
yield return new(1.0000000000000002, -5.551115123125783e-17, 2147483647, "1.00000000000000000000000007754818246295752453608749509733042963658391677914710093852832723311784141110801354996083710043e+0");
yield return new(2.0, 0.0, -2147483648, "9.99999999677228191658816707807941718273741041868880112443673806175630383600978533229555906993761769452269156830452576800e-1");
yield return new(2.0, 0.0, -2147483647, "9.99999999677228191508514371983406113774081566473621756422724422138637696671801252012588344215482041058766213312547646733e-1");
yield return new(2.0, 0.0, -1000000000, "9.99999999306852819680281197486364482220171244240985209083889726852474576362142530175522871575565518796328395279731427276e-1");
yield return new(2.0, 0.0, -65537, "9.99989423630696199059051957856192099125312797258828893503844008959857591057955446665637936075276031946365194145573704209e-1");
yield return new(2.0, 0.0, -127, "9.94557015198170698569268684444940799852443429879734200797346759717920936017114553597195565202843484576595299109473455393e-1");
yield return new(2.0, 0.0, -4, "8.40896415253714543031125476233214895040034262356784510813226085974924754953902239814324004199292536172801573743519085080e-1");
yield return new(2.0, 0.0, 4, "1.18920711500272106671749997056047591529297209246381741301900222471946666822691715987078134453813767371603739477476921319e+0");
yield return new(2.0, 0.0, 127, "1.00547277302221306857687621252345877639796444631811870580077371310514545554465530820363514238115898312615622360304681075e+0");
yield return new(2.0, 0.0, 65537, "1.00001057648116457167215270349394297497241259861435957360758872150558426213383522387510316657634694682655547830200490957e+0");
yield return new(2.0, 0.0, 1000000000, "1.00000000069314718080017181643183694246616754030093671697092943849725637366367267953869962182007700078613681030647808347e+0");
yield return new(2.0, 0.0, 2147483646, "1.00000000032277180874596960446862733311713999415756678394113658865613268338817468717932028873473804903097531409749939483e+0");
yield return new(2.0, 0.0, 2147483647, "1.00000000032277180859566726840708505646978792137378497109441088492007257435200513095235613984898693606233800546941310214e+0");
yield return new(2.0, 1.1102230246251565e-16, -2147483648, "9.99999999677228191658816681958547584335082611430561169599513723728424576856580718257565489544452237705020459069947882164e-1");
yield return new(2.0, 1.1102230246251565e-16, -2147483647, "9.99999999677228191508514346134011967798360985780333847069259966358007585839150630790421628883798553926885874690633350153e-1");
yield return new(2.0, 1.1102230246251565e-16, -1000000000, "9.99999999306852819680281141975213289439743716051207237584016731569655874945765337488321362949664530428226184353002739314e-1");
yield return new(2.0, 1.1102230246251565e-16, -65537, "9.99989423630696199058204946791834226098574610666386572491828847335444542638143989678076385300355485813923979841750273381e-1");
yield return new(2.0, 1.1102230246251565e-16, -127, "9.94557015198170698134552110596326302127783098519685937928255477363535398448917798333762097169753147298321816070224779651e-1");
yield return new(2.0, 1.1102230246251565e-16, -4, "8.40896415253714531361343456990333037273930712339605549095474201561442065765215381166952041912395670554795993244453114896e-1");
yield return new(2.0, 1.1102230246251565e-16, 4, "1.18920711500272108322106397211144253151700263219769591685932458823782379339473073361605670763436196622268764794342588972e+0");
yield return new(2.0, 1.1102230246251565e-16, 127, "1.00547277302221306901636401694982289517784785134816249915056041303670059954911434134627676603120430745968084045413102624e+0");
yield return new(2.0, 1.1102230246251565e-16, 65537, "1.00001057648116457167299973247518873304141849883167012046891747302097388986443424912520447961621697886872761750815480667e+0");
yield return new(2.0, 1.1102230246251565e-16, 1000000000, "1.00000000069314718080017187194298821220139099965691445010273915595952653528761128675944481872060407331086318118900230989e+0");
yield return new(2.0, 1.1102230246251565e-16, 2147483646, "1.00000000032277180874596963031802150781683415374781615446699803468236660406997413991920105072595167754108588634585506967e+0");
yield return new(2.0, 1.1102230246251565e-16, 2147483647, "1.00000000032277180859566729425647921913241990395778034842566910391711533869444297548606106827235729454681568717045695391e+0");
yield return new(2.0, -1.1102230246251565e-16, -2147483648, "9.99999999677228191658816733657335852212400907236826728936861121512973786603656151560277458862782107652486186439797745912e-1");
yield return new(2.0, -1.1102230246251565e-16, -2147483647, "9.99999999677228191508514397832800259749803582096538007616394135964059256375774369722751252898451082054434045823284486837e-1");
yield return new(2.0, -1.1102230246251565e-16, -1000000000, "9.99999999306852819680281252997515675000601853918675145724379782832109454254783937588221793532867457360688739145176975556e-1");
yield return new(2.0, -1.1102230246251565e-16, -65537, "9.99989423630696199059898968920549972199070260574720767322569567178470058265290262829574351639957004412491555235384479958e-1");
yield return new(2.0, -1.1102230246251565e-16, -127, "9.94557015198170699003985258293555321898733970897434306052301125579488231708164339464417484061410564127086230309536355374e-1");
yield return new(2.0, -1.1102230246251565e-16, -4, "8.40896415253714554700907495476097562559930944880181644734524190847731185308115660697649093610986168995665907658876800146e-1");
yield return new(2.0, -1.1102230246251565e-16, 4, "1.18920711500272105021393596900950861197006369410120625642703806574943503453747826178059735330634342092423949965450872130e+0");
yield return new(2.0, -1.1102230246251565e-16, 127, "1.00547277302221306813738840809709463341370528576464459476982998907441893942517828030474412174779066254340522694470462722e+0");
yield return new(2.0, -1.1102230246251565e-16, 65537, "1.00001057648116457167130567451269721685638786197236446798966114220236890746013124072082464199198066373613250759927643514e+0");
yield return new(2.0, -1.1102230246251565e-16, 1000000000, "1.00000000069314718080017176092068567273094099945704890982500979394154682043200087244578517712524562419087483025384465692e+0");
yield return new(2.0, -1.1102230246251565e-16, 2147483646, "1.00000000032277180874596957861923315841744439963768881345658549678415474062183391932658583519852145530978327249062697717e+0");
yield return new(2.0, -1.1102230246251565e-16, 2147483647, "1.00000000032277180859566724255769089380715450386016166199564128562351107643004736633916818878270333313519693411293572981e+0");
yield return new(81.0, 0.0, -2147483648, "9.99999997953675151720548274836456532846885478889096519484409535509045204346367372146476496522683428992246955854065620638e-1");
yield return new(81.0, 0.0, -2147483647, "9.99999997953675150767654012268072453125196352681260641331020911522249834643826735151670871497223370405459354207594595431e-1");
yield return new(81.0, 0.0, -1000000000, "9.99999995605550854983152906776005982396860509500372960475969270082893070170480731288048724776245830001726339865584975174e-1");
yield return new(81.0, 0.0, -65537, "9.99932949298448561454012152926320641635328645269371540884789610568942854964509927145683761076933578936718300260987356479e-1");
yield return new(81.0, 0.0, -127, "9.65989840555887148969217550077403971354516363872179681774450522143254931517680366106189052793449473619326327704543324247e-1");
yield return new(81.0, 0.0, -4, "3.33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333e-1");
yield return new(81.0, 0.0, 4, "3.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e+0");
yield return new(81.0, 0.0, 127, "1.03520757467236033530543471288810022494088079600069305193374347011395988538050538471388319049046761733844078421133773453e+0");
yield return new(81.0, 0.0, 65537, "1.00006705519764948361423716244794675338601948936662461164012021889870230765706274666593292157958282024079296414803229970e+0");
yield return new(81.0, 0.0, 1000000000, "1.00000000439444916432803046622530569418042241089634134263709292574059098835890918049534151352113171108211517096641746995e+0");
yield return new(81.0, 0.0, 2147483646, "1.00000000204632485437268565224228310592409670452122348099825563365809215156076135811360127268551268339371689581311981503e+0");
yield return new(81.0, 0.0, 2147483647, "1.00000000204632485341979138488658465545255815924147291691528125897789412620393583797475378619089111778411488026131876557e+0");
yield return new(81.0, 3.552713678800501e-15, -2147483648, "9.99999997953675151720548254412243919011146830770935958350900360773622300802850967916662690335507295568393937424277670883e-1");
yield return new(81.0, 3.552713678800501e-15, -2147483647, "9.99999997953675150767653991843859829778692581590703910114487212520803992317733131791965726152853321266366539486765331944e-1");
yield return new(81.0, 3.552713678800501e-15, -1000000000, "9.99999995605550854983152862915343473899560964452044574553905506851944282433824814712831727288411868278387266594299252229e-1");
yield return new(81.0, 3.552713678800501e-15, -65537, "9.99932949298448561453342947411679505757177889008659801705464697829300832828769143728393791748793890372077230477548931972e-1");
yield return new(81.0, 3.552713678800501e-15, -127, "9.65989840555887148635603734569946492320255693200169929676175951815336380909892128732789953342772860889871488119942584837e-1");
yield return new(81.0, 3.552713678800501e-15, -4, "3.33333333333333329678278108229937415673471064382680099801478904651305169342611206833234474930584025521058313804092610036e-1");
yield return new(81.0, 3.552713678800501e-15, 4, "3.00000000000000003289549702593056361964333528155806054292362775870476646462356741677018423459517913648800660398250581685e+0");
yield return new(81.0, 3.552713678800501e-15, 127, "1.03520757467236033566295353382096553818723844000461087086238289608233192459410292214131865526216439513474217866131813643e+0");
yield return new(81.0, 3.552713678800501e-15, 65537, "1.00006705519764948361490645771301300939842250502944446176628212170658921357479799289060217785312223286655626511873850904e+0");
yield return new(81.0, 3.552713678800501e-15, 1000000000, "1.00000000439444916432803051008596858816462621762544084455281511052671439785814350475153187174225080408952640379644598318e+0");
yield return new(81.0, 3.552713678800501e-15, 2147483646, "1.00000000204632485437268567266649582237051357618387727952830257732690495549543553915311068681404724698204100289880701364e+0");
yield return new(81.0, 3.552713678800501e-15, 2147483647, "1.00000000204632485341979140531079736238820982122567978006018092670841577912727902929236265745260752401624662049341836864e+0");
yield return new(81.0, -3.552713678800501e-15, -2147483648, "9.99999997953675151720548295260669146682625022826757891642473180391014620268366545751514497642613536170654263652711765849e-1");
yield return new(81.0, -3.552713678800501e-15, -2147483647, "9.99999997953675150767654032692285076471701019591318600720570558586823316669893771456364761008584725062347804698132478197e-1");
yield return new(81.0, -3.552713678800501e-15, -1000000000, "9.99999995605550854983152950636668490894161978306427408311952692975143906557073176403287965435996544008442485174277679296e-1");
yield return new(81.0, -3.552713678800501e-15, -65537, "9.99932949298448561454681358440961777542831646751618930226946955647716641453205118777190111722327662820710663818812238640e-1");
yield return new(81.0, -3.552713678800501e-15, -127, "9.65989840555887149302831365584861465136516785797409441419449282016696677202308000477989427384640472600349948769479087080e-1");
yield return new(81.0, -3.552713678800501e-15, -4, "3.33333333333333336988388558436729451384626080618540601242204802808724919275262667286949180105243198761208525632285528775e-1");
yield return new(81.0, -3.552713678800501e-15, 4, "2.99999999999999996710450297406943529824294013543534767144048022101150474947433251744180451014309821010093857265247169471e+0");
yield return new(81.0, -3.552713678800501e-15, 127, "1.03520757467236033494791589195523489613698327641020925033316214815623990875574587118576713418208378841043639647481712243e+0");
yield return new(81.0, -3.552713678800501e-15, 65537, "1.00006705519764948361356786718288049734426118776131224904095546417634770768519231673882800203230037693861291725943726682e+0");
yield return new(81.0, -3.552713678800501e-15, 1000000000, "1.00000000439444916432803042236464280019621668040950271861183269824978669778445336689214327319445823513512431270481642420e+0");
yield return new(81.0, -3.552713678800501e-15, 2147483646, "1.00000000204632485437268563181807038947767893703906520516823309322645725463387513845431586644786635676434459238880421942e+0");
yield return new(81.0, -3.552713678800501e-15, 2147483647, "1.00000000204632485341979136446237194851690560143776199361887049838641682019884703637662642561544491139338887661467725135e+0");
yield return new(1e+308, 0.0, -2147483648, "9.99999669754835060867398723195218791326991427498538637371948190354122788311804960394428617533500866151893426408670645847e-1");
yield return new(1e+308, 0.0, -2147483647, "9.99999669754834907085035411819516293942997025201092362487693165134292211930637518666855779147038604526207326933076186678e-1");
yield return new(1e+308, 0.0, -1000000000, "9.99999290804042837405656066400712605042434016975548903742491073332714697157573471301606460038262639354756235897027851526e-1");
yield return new(1e+308, 0.0, -65537, "9.89237029518914741707925570122451348700382476019331160034738533886864989404239803017703333034176438925901825317363884388e-1");
yield return new(1e+308, 0.0, -127, "3.75667089031159721342801022673693733746820734892571643635301261952019300174152540960672418138554228805803270984627109828e-3");
yield return new(1e+308, 0.0, -4, "9.99999999999999997255234092639886164483118941322693002202644466118298883688905501765798127653785006601604392452173303686e-78");
yield return new(1e+308, 0.0, 4, "1.00000000000000000274476590736011384305062094488369602433687160865381803822283746860825719590929513268274096591178940102e+77");
yield return new(1e+308, 0.0, 127, "2.66193134612613073758250499198827902827076725914565617542164746424082297594721007507115768273386965561918862293063279447e+2");
yield return new(1e+308, 0.0, 65537, "1.01088007237893177201699504326060747428284587692839590936991112888267274372447738613818581891247782590566552411502701205e+0");
yield return new(1e+308, 0.0, 1000000000, "1.00000070919646012185669637742884518833990916296723151534878606554095918526875396003177140962595763319943478196366700898e+0");
yield return new(1e+308, 0.0, 2147483646, "1.00000033024527430860251402810208452903269459333607441020022743177379303006250203974777017702808163183034198605526344277e+0");
yield return new(1e+308, 0.0, 2147483647, "1.00000033024527415482004900169106508666844503918180778637396065330366093947941333214951754596932587856763218421901912301e+0");
yield return new(1e+308, 4.9896007738368e+291, -2147483648, "9.99999669754835060867398699960587269488885148601539419155133556341818996071592829581100730034690703366769368458499898971e-1");
yield return new(1e+308, 4.9896007738368e+291, -2147483647, "9.99999669754834907085035388584884761285425889763503864534801553131791167983542893071462869729292249885354224498709524667e-1");
yield return new(1e+308, 4.9896007738368e+291, -1000000000, "9.99999290804042837405656016504740252721407122286880922225880153258800673517830122751490396865002670206734381087157705765e-1");
yield return new(1e+308, 4.9896007738368e+291, -65537, "9.89237029518914741707172423373592598114221536618186055045083850269007290251950970328259376749450521294485484233404572523e-1");
yield return new(1e+308, 4.9896007738368e+291, -127, "3.75667089031159721195208203922914809163940501735404837185321378469127304072823502683528326598792173658912533397790110228e-3");
yield return new(1e+308, 4.9896007738368e+291, -4, "9.99999999999999984781232158047887901823676337415865078929218046142145311665451458644883601390276065847030561419372147855e-78");
yield return new(1e+308, 4.9896007738368e+291, 4, "1.00000000000000001521876784195211232978721828981988568931547300757177242546605561282654673053122176800730435966797549587e+77");
yield return new(1e+308, 4.9896007738368e+291, 127, "2.66193134612613073862832977187263925710630129895927392740024124361108940300066515378163418252354233894569812884551807010e+2");
yield return new(1e+308, 4.9896007738368e+291, 65537, "1.01088007237893177201776466774622599782007594774654862571682344726723325714727413419229972038135939041865672529683398500e+0");
yield return new(1e+308, 4.9896007738368e+291, 1000000000, "1.00000070919646012185669642732488831277996492510914472434753542052742785401708380986217157671797255184217885767139237517e+0");
yield return new(1e+308, 4.9896007738368e+291, 2147483646, "1.00000033024527430860251405133673141876679967917468123056251212486346698276480535564537386580046383335106848530694849615e+0");
yield return new(1e+308, 4.9896007738368e+291, 2147483647, "1.00000033024527415482004902492571196558307096608776916110219302203522257370944547201576225545134624605282600400251122932e+0");
yield return new(1e+308, -4.9896007738368e+291, -2147483648, "9.99999669754835060867398746429850313165098865710892607200161129428667556698395119353159299532370764291482054059113003320e-1");
yield return new(1e+308, -4.9896007738368e+291, -2147483647, "9.99999669754834907085035435054147826600569319954036151900085791921664093585886494878737456720420404189753746498974298498e-1");
yield return new(1e+308, -4.9896007738368e+291, -1000000000, "9.99999290804042837405656116296684957363463401274041979674462458858591386950189895673755627152713728449493453584090815549e-1");
yield return new(1e+308, -4.9896007738368e+291, -65537, "9.89237029518914741708678716871310099324123004831188229375606957187263442161685531435202413254854469052246022940941769308e-1");
yield return new(1e+308, -4.9896007738368e+291, -127, "3.75667089031159721490393841424472665751979949105648024816706058038805060922799845533697189074027552411648793543608157728e-3");
yield return new(1e+308, -4.9896007738368e+291, -4, "1.00000000000000000972923602723188520514618286625414810014485384087593930960680082100760109027835573669796708623596338045e-77");
yield return new(1e+308, -4.9896007738368e+291, 4, "9.99999999999999990270763972768114889511850807332727492031621711279322588573758160280977540651371629098482878910872212292e+76");
yield return new(1e+308, -4.9896007738368e+291, 127, "2.66193134612613073653668021210391874766363758885197898567839813469119065960724837274531596184798498430264585076455605709e+2");
yield return new(1e+308, -4.9896007738368e+291, 65537, "1.01088007237893177201622541877498895070721520276689014312641166889738336580541516930327584914132043615865163819711601497e+0");
yield return new(1e+308, -4.9896007738368e+291, 1000000000, "1.00000070919646012185669632753280206389985091121196694495912762956405919089982604860207784579421412784422273606390519619e+0");
yield return new(1e+308, -4.9896007738368e+291, 2147483646, "1.00000033024527430860251400486743763929858834818134712126578357394720908634167015276320944363533889855487877645102553964e+0");
yield return new(1e+308, -4.9896007738368e+291, 2147483647, "1.00000033024527415482004897845641820775381795295972648292238445627641681505133451796063777800312059523263663117065260742e+0");
yield return new(1.7976931348623157e+308, 0.0, -2147483648, "9.99999669481722826465117435386976948342107936031989673865218868781888935318907599395825073603473067968269630805495549131e-1");
yield return new(1.7976931348623157e+308, 0.0, -2147483647, "9.99999669481722672555576371318018995851073920880658770768273929732126297892521930138317645770719010289957523033659774987e-1");
yield return new(1.7976931348623157e+308, 0.0, -1000000000, "9.99999290217539002306167341463050715678964286598129188091549109572144398346147322215063776616206069393783375690468680537e-1");
yield return new(1.7976931348623157e+308, 0.0, -65537, "9.89228176670490881197007837638381180315076928480059686472643713938449857234504315081834645364772235512522141232078817501e-1");
yield return new(1.7976931348623157e+308, 0.0, -127, "3.73936204240359858431392626661716510073843597455000769515838556045473967372702485958018207961023474321002798177436901061e-3");
yield return new(1.7976931348623157e+308, 0.0, -4, "8.63616855509444486508818122304097055415111538584065636011289536797300988764707973121273069940936828291643022408397584041e-78");
yield return new(1.7976931348623157e+308, 0.0, 4, "1.15792089237316192209694896490707222964874608799567844200466962889572704321535999944925308227539057659339055339681605199e+77");
yield return new(1.7976931348623157e+308, 0.0, 127, "2.67425295721624466668055053864756772962064008507350326031301900994352702596675163325520840634150917319380015469674320560e+2");
yield return new(1.7976931348623157e+308, 0.0, 65537, "1.01088911899554311098818861889612056746965306444907634348051101306639917879372594731419735820185731890890822809484680683e+0");
yield return new(1.7976931348623157e+308, 0.0, 1000000000, "1.00000070978296478919335497166435299517459580890220582295692617067802908675450037170016139538617087580132809475448149745e+0");
yield return new(1.7976931348623157e+308, 0.0, 2147483646, "1.00000033051838672372182067663744348054125113142207904822572636386893043897582061214864025605254492371298344050311243108e+0");
yield return new(1.7976931348623157e+308, 0.0, 2147483647, "1.00000033051838656981217772934576675933587458237208296338252492692852030276973055222630198026689392817200222219541819761e+0");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, -2147483648, "9.99999669481722826465117422462284149049658203977730691177620160558888071923363689156319793389648787326081551919429569180e-1");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, -2147483647, "9.99999669481722672555576358393326190540097088282409121061392394844297088507070939850864310072342127536314546297804252483e-1");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, -1000000000, "9.99999290217539002306167313707494800470814948378621791700279348334837580755188241247627918081203557890519997469061018852e-1");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, -65537, "9.89228176670490881196588889602024793385177044152075281205143852298138618576294969087668520599513209425064709197880671026e-1");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, -127, "3.73936204240359858349669677116719925636364968738969014814908093023397831213385346395914573071697673511328898574089570503e-3");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, -4, "8.63616855509444480516272388298082468976940693701934262155287904721286694029555962874437939859727373029276969525682272167e-78");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, 4, "1.15792089237316193013163918620202419275311801113039901781425408506670203535359999986930517479777335259071819175446281896e+77");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, 127, "2.67425295721624466726500274470532142872794291011613109790485913995178327028896408890294568583629575992882978266237211471e+2");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, 65537, "1.01088911899554311098861674055840747859184875149885269247373796221765482950460451708695544232859017529865473543970267747e+0");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, 1000000000, "1.00000070978296478919335499941994831123826190752762875639444000431905690742327147179518382792974418227711949696424995972e+0");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, 2147483646, "1.00000033051838672372182068956214483556940079530743192705761129327807565311777327614952935547282627768771645543655684361e+0");
yield return new(1.7976931348623157e+308, 4.9896007738368e+291, 2147483647, "1.00000033051838656981217774227046810834548918316883553964492793630628632323132425672291210898445292652634170108073959678e+0");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, -2147483648, "9.99999669481722826465117448311669747634558026818537123136722509442160033001556055975288557886342597385486604895967609593e-1");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, -2147483647, "9.99999669481722672555576384242711801162051112211197054106744586493125129972911899794292358589724168692341172107359001592e-1");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, -1000000000, "9.99999290217539002306167369218606630887114395189068313230507578843306757324746751957548790202205932204621027519424635239e-1");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, -65537, "9.89228176670490881197426785674737567256605134138840685551561682096314433807473036412309984821565045034747139504294652296e-1");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, -127, "3.73936204240359858513115576206713096797450105859477999411756817464599318795695017805428527798671744461550576596422600464e-3");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, -4, "8.63616855509444492501363856310111849761477696364233315980107827284676758712602285586084378478360279452396707828870307249e-78");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, 4, "1.15792089237316191406225874361212009928878517588125180875116688662481229316095999831925379112362843167644711836553553909e+77");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, 127, "2.67425295721624466609609833258981401441926062799468182846853218111582562553339344952182228179908048391557972406096353261e+2");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, 65537, "1.01088911899554311098776049723383365633557479554281695199304619195521971101638070966545626044858958454134518817634965355e+0");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, 1000000000, "1.00000070978296478919335494390875767911092893990425910809106322596711849375098075545811812979336642245362364623466143971e+0");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, 2147483646, "1.00000033051838672372182066371274212551310110880420056753635878098051014528956787702795952197194570487070061209584818025e+0");
yield return new(1.7976931348623157e+308, -4.9896007738368e+291, 2147483647, "1.00000033051838656981217771642106541032625962284280495231054414736561982135841189956819866041115979121483252410318300874e+0");
yield return new(-2.1249313539597645e+148, 7.098034416949286e+131, -2147483647, "-9.99999840959763964179712921675691428515177718723571125780356831687858337194530199366841567701767531684180057320600539734e-1");
yield return new(4.147625292410626e+216, 1.5309010345804195e+200, 65537, "1.00763971146755381356101977036748995722022832175355406561587388662802510175034004187147375035572203474817319914211890904e+0");
yield return new(-6.723931397868513e+213, -0.0, 127, "-4.82705306355030649541384882095489431503608390910772341499236257472207239776711667187328298638897436079156939466143776782e+1");
yield return new(2.0719930910441882e+176, -7.029552803973744e+159, -2147483648, "9.99999810949224521798166743711040733472235756808201697990471160035993573185085833146199024328917202123788955401907082392e-1");
yield return new(-3.3000510598222188e-99, -0.0, -127, "-5.96272416257708074819707187424664138298406053061723556918481976790686544194847136104871206302072128236666876371239128647e+0");
yield return new(6.130809850815003e-231, -2.860444667617094e-247, -53170702, "1.00000996952178597269401615194114127424567017535626214352498682835322932680726382255436788740947477760137973996366756928e+0");
yield return new(1.0673131375869938e+265, 0.0, 1000000000, "1.00000061025038025015090687096894188043000474816658796696872180079595604871392884901404633292112522859287160273558300629e+0");
yield return new(7.571974537692792e+227, 2.1040543606193494e+211, 2147483647, "1.00000024433775540814709993203556477142342652538709914250421563870957566249488312232755435709274304465678630881380442052e+0");
yield return new(-6.710722759246113e-258, -2.310648801106982e-274, -127, "-1.05921974655416148266259807170021903487171620132278754624074405537762865610447581686271516136378559574900914136952384983e+2");
yield return new(4.4219592497775426e-302, 1.295163e-318, -4, "2.18070197038685288902412936528224870839186931685767063796651126203731881886399195485722555076600676063807363859488083148e+75");
yield return new(5.4503591398500374e-179, 1.712351053912815e-195, 2147483646, "9.99999808861403122340379966561346260951072969762338308700177635445127758284926222248627773674083017121470554595737059345e-1");
yield return new(1.9788769873e-313, -0.0, 4, "6.66967542059683665866478315331038565571951139888143170901431132901687415366540334211174115503638858465164768069764946390e-79");
yield return new(4.150578829354881e+228, 0.0, 4, "1.42733911242931247429577876386562625555391280785402393751085960246020631826474043170759793748194238395109051675300160075e+57");
yield return new(3.1411739986208755e+244, 0.0, -127, "1.18802627899609840586180371314299308798100247692469762386569922845562671535199037531971188200436874453678195425031896091e-2");
yield return new(-8.53402411825752e-293, 2.781342323134e-309, -65537, "-1.01031441369176016576967882174010708468727721476587298167757702115437890155267583089462561862575444494310152437126750570e+0");
yield return new(1.879983117875532e-78, 0.0, 2147483646, "9.99999916660428435723604792884923782999102066850050801736796396704762313932397550766648884955754721895217582610630103528e-1");
yield return new(2.7085161747010475e-104, -7.745183829698637e-121, 4, "1.28287061770418613919758339932187218888180987652433606278863966228345395667101239913544061761004265525949095359522423004e-26");
yield return new(-1.146787127910177e-157, -0.0, 65537, "-9.94501206405199288572143322681749502751866013332995552873909125174983998263146163002403000642369556374492818074932925528e-1");
yield return new(6.095063458890788e+165, 0.0, 127, "2.02019610473746063644113231143831566578614369963072931079608925885278036059691214673576519332238370795516744981732185760e+1");
yield return new(9.419192233451517e+271, 3.75375841440235e+255, 65537, "1.00960136896884983332153416368889555175398504957762309840108452367861317987951740784721255099720995866244099475129384015e+0");
yield return new(-1.9284912425757565e-264, -5.509016039626555e-281, 65537, "-9.90777403547877938857947185871148394545659670700306921262686248205798360370672564111186426509274736954311796100019763240e-1");
yield return new(4.537464672119312e-31, 0.0, -2147483648, "1.00000003253471600151508957129925679543386258946918407983548660837927682091867286971768423599429958543220440960502244458e+0");
yield return new(2.074671233602811e-40, -1.0195788231247695e-56, -2147483647, "1.00000004254914960207101566481974026852769930674618737351465375027363016070691350819341436531561259481379421466054037733e+0");
yield return new(3.782603411569593e-284, 0.0, 1678787032, "9.99999611264791411916583072884556276528610707320261433869695743811086052422178350392875695442003904089456692599002314350e-1");
yield return new(2.6616983223047094e+121, -1.1467493079950358e+105, -2147483648, "9.99999869804949295874918922284487208644069629167057527270641986535082738387266594843180725512794064572965094105764792431e-1");
yield return new(4.825008666001236e+245, 0.0, 1000000000, "1.00000056570732032671875016807413753414765153117520087708394175074792027588990170328600460087926335806169666445097028526e+0");
yield return new(-1.5e-322, -0.0, -65537, "-1.01137135116170984240934522864436163916836395622381981239847760066875578196608025327220015655646529097907724124018110612e+0");
yield return new(8.844526457207711e-71, 4.021529366771898e-87, -2147483648, "1.00000007511291134744092917798352066633252091438029252481430366772388051227176237362623066399710195206004044994556290920e+0");
yield return new(-1.1787287859059328e+129, -0.0, -2147483647, "-9.99999861606446501249297209346091269794233555102852065202749688038741272191181079531538877469824452000169406703683226833e-1");
yield return new(2.3350871320145874e+180, 0.0, 2147483646, "1.00000019339537550305130301742589754522576267979304110089461106484148629948218728903316643333187870183496866196900863674e+0");
yield return new(-5.737115427319538e-215, -2.5764595078388533e-231, 127, "-2.05617739071458567917218646485167850251579039481661277943517266484366258646370472457755736536256468593145852187610735573e-2");
yield return new(6.232446458923528e+147, 1.7745086042373215e+131, -2147483648, "9.99999841530923428205499510049696468394303333502304939200623486234522701253580555760436868800603798465892158669820479774e-1");
yield return new(8.86758227496169e-85, 2.8574684782056875e-101, 4, "9.70401159215481242958894596465183172264702128871277134121554110391166902608968169099277300707752299732738646510049898011e-22");
yield return new(1.4652643542498693e+241, 7.40298315191607e+224, 65537, "1.00850915199303793446218185009094172923619467801463183307008625882587081879256845894899134910986077117616579793198678057e+0");
yield return new(9.942317307166815e+135, 3.227812347608636e+119, -2147483648, "9.99999854180140026961534388398578738485909789196093559996464369495464048983163023862748688498228642539386242086511605514e-1");
yield return new(-3.913734663518928e-150, 1.3566642758087631e-166, -127, "-1.50119216248492058529247361242447879462074991235942344876928947155856098146907556730116610938716387154966633746595211178e+1");
yield return new(916490419270.2593, 0.0, -4, "1.02204029269375628922508541744587141595150707924840493727978386568825257230768330044522076847231386056741188096207033492e-3");
yield return new(1.9038429550492584e-208, 5.403227209783267e-225, -2147483648, "1.00000022272294301301616871872044096804599391197663863566625894798433795617689637727708932167462312873587564170225541740e+0");
yield return new(1.626982079293464e-126, -8.200532357869981e-143, 2147483646, "9.99999865126341748491073661679962863415533786512719346489319213481823788161475499644878346102476165022677695366369701146e-1");
yield return new(9.447758514200265e+80, 0.0, -2147483648, "9.99999913176253035490573610006805571817927543590094876509804010780006538578081188614978928528826277934113938899202365569e-1");
yield return new(5.396284183652472e-194, -1.520873253036128e-210, 1000000000, "9.99999554984301581243033862324002124786986989493302527202460182455595352186605190715821128233682799524266195830635327728e-1");
yield return new(4.3199835477524154e+95, 0.0, -2147483648, "9.99999897457275475428768039101163249022352314370889690214826632436071737776425349061702177724925914302226849643696622297e-1");
yield return new(4.5872338840381304e-288, 1.8227805048890994e-304, -65537, "1.01014651504979221720668658338108968123272232998008247778126815819597417542793825031315875640294302428386629660815427417e+0");
yield return new(1.2068303045666464e+237, 0.0, -2147483648, "9.99999745795226193124934994066765071631871383912168307491429321374387316571782529547607144613543664269456211614675397633e-1");
yield return new(4.7625512484234156e-88, -1.3952482803738708e-104, 1376157086, "9.99999853892631852063577219837254098756897104046550152723519593693575903527392459510082430765824993678100299233533025416e-1");
yield return new(1.0513145438304164e+270, -2.932623761251836e+253, -4, "3.12296294983883607851336713752414784201127948516079717906180320823257000669955357806200044373499489855749221265736950030e-68");
yield return new(5.589422625399524e-209, 2.7016136048916335e-225, -287493552, "1.00000166793239364143231691107584263920961145635638194715363314972530397861177096817329061949894289808780228167099545492e+0");
yield return new(9.615901074926444e-33, 0.0, -4, "1.00983984674504747176184394157868224681809918602354860972647042167880073495413474904683668927311521592203203546840513572e+8");
yield return new(-1.4012400209486434e-48, 7.596454196607839e-65, -65537, "-1.00168270452972400782734718583655504768846563273007622173469378155886027962955040980088132719478098658430261762146121159e+0");
yield return new(3.0221268469846867e-89, -8.720301752336693e-106, 2147483646, "9.99999905087005833148610228969076804861474126455243503205524487577298430899247839908037055646833423327992237755515911757e-1");
yield return new(-8.112565918300184e+307, -0.0, 65537, "-1.01087684601302523180576498789829049312508009625204767473744208676403325568780073007238549378204405092655945868643786569e+0");
yield return new(4.9935058803537105e+169, -1.6759759912428246e+153, -2147483647, "9.99999818045187930911224446224482494300714248375911104055452182966626158801625625828244203767999083958280689899711932406e-1");
yield return new(3.1490401162258356e-16, 1.232595164407831e-32, -4, "7.50681048108497054324406404249243435200625398767498323401986335673087607760661351855173437240522820447015177386211811797e+3");
yield return new(-3.711354237109857e+159, 1.9510928439474951e+143, -1696990351, "-9.99999783485871544903782361780700641036204683545478083114994805712095431762655829454281094820910460066602574183740225785e-1");
yield return new(5.7309522218258495e+54, 1.7014118346046923e+38, -2147483647, "9.99999941286876295835265117247595151130920191781550351733086706866642724199221699806648402627879594439089323961256900331e-1");
yield return new(1.5097634622323465e-198, -4.641336831775293e-215, 4, "3.50531642383722160523262156357848280688354417986285681432244990751101959271496262851673832398644174941536798059859087175e-50");
yield return new(-1.306744008329486e+82, -4.2124916667422875e+65, -127, "-2.25639860574197311275127836096912955860498225890896759803965714537679468930051846938231596555251151627951278618600141405e-1");
yield return new(3.34331251507222e-09, 1.0339757656912846e-25, -1000000000, "1.00000001951630394090106065437907584290293576107256441134586317200311441575920883468529736876207801527320150943790203606e+0");
yield return new(-7.7183963962973715e+177, -0.0, -2147483647, "-9.99999809264610889491610122352101269808108060370249749234926740489078549833848552947839319435827082391470081448810832648e-1");
yield return new(2.4746306664250554e+229, -1.3465947907963836e+213, -264698264, "9.99998004529601097627222871737544495143911415613166764290120809034966902699848214806513821333738538094018135643019116138e-1");
yield return new(6.539020872461836e-39, 3.2626522339992623e-55, 127, "5.00420100578283124538413626088226788750431023492643460708634689309751940531689993467162302884615936318471074965479617955e-1");
yield return new(7.370176192288791e+114, 0.0, -65537, "9.95972364542456800622180565560918857306928177692353341371133294656266258220093452978654461653130140664353767395207945668e-1");
yield return new(3.672438494588782e-258, 0.0, -4, "2.28434327527581428547537990347253839001328374006662709886425144021656532257826014871037562906367464086527751349161331674e+64");
yield return new(3.7946133980463076e+306, 0.0, -1000000000, "9.99999294075628174287847828145404398040555810945548414332025537979689241000862781965316611278042618779273314873642185108e-1");
yield return new(5.214312182655994e-233, 0.0, -2014436629, "1.00000026550896800825660566661370448515587366060346784619323074507712987415985543738345440665041084696192119429780869485e+0");
yield return new(5.482507924459564e+164, 0.0, -2147483648, "9.99999823362806627413350777611240471330272282369558603747243546863527948017347530189340060725638769191057061046348743426e-1");
yield return new(4.791478301944406e+90, 2.261564242916332e+74, -221640664, "9.99999057937315127854056660282027850679325658330346746760387138587814098157070692943467936015076398246996664446012532096e-1");
yield return new(7.094453661392563e-208, 2.161290883913307e-224, 448787579, "9.99998937185085132390741206105477340106059076021932449319402386181584592483093831211054543454803803179561038489268359922e-1");
yield return new(-1.5299279906036949e+162, -4.994797680505588e+145, 65537, "-1.00571448207764218254394335724791622004661125124897421569807375515534060138267705749766151486113634675862726821655194354e+0");
yield return new(2.1956345791758541e+133, 6.304320991423117e+116, 2147483647, "1.00000014297212966086444707305936402291520154065936241728804774840618710195577821638039418000261317353473072729477195680e+0");
yield return new(1.363223602490958e-65, 0.0, -1000000000, "1.00000014935819000756943197112251515000670928296411213060681825637761834366568255807272246114125508576637183785253781991e+0");
yield return new(4.139015047904233e+124, -1.1742712913869166e+108, -1307873984, "9.99999780605026620859373986278204453882845217141126956645379842118844267825460673797353777003094719085659362917360916389e-1");
yield return new(-3.0476766336554833e-177, -1.0959046745042015e-193, 127, "-4.07483465690623096144982821089593883294576565812933472885300486643125559704468985861045809256160770039774448763706252522e-2");
yield return new(4.2413190257712564e-109, 0.0, -2147483647, "1.00000011619968127401343762179001090324301417297472518286050965237911121334465301501150476612005267236557227826367327542e+0");
yield return new(1.717425831066361e+229, 6.732973953981918e+212, 2147483647, "1.00000024579133743873458028998613473024927167247685714936459519996729470213564659698034334840469739485200339319830620054e+0");
yield return new(3.638111390158817e+69, 0.0, 2147483647, "1.00000007458489488924001547879127636700692262115764720462803219525978265146128734105039369825667775325164971837562487525e+0");
yield return new(1.0345198924418813e-164, 0.0, -127, "1.95534109916375607675029254620595780865017064859500712835689868164702653559566991115773183726212212909385133179152053015e+1");
yield return new(2.7444129262342785e-214, 0.0, -127, "4.80382316585726757792338360424348090562028880381675459618443378188496551100766249787536001988031521453566842883761552636e+1");
yield return new(7.438051478184081e+150, 3.6341936214780345e+134, -1000000000, "9.99999652605687475861141057571799724595441166283463672002265613592389361373386045452200025149274744144210055520736947380e-1");
yield return new(-4.2597739132243285e+102, -1.2433080910244666e+86, 2147483647, "-1.00000011004177326543213484812227885632383510083155670501530271424581092244430106134733927517918711933489596954654052577e+0");
yield return new(-1.5706963483578736e-198, -0.0, 2147483647, "-9.99999787909779106008527685251180855950833593562688359568846346430694972350275818050472003356017315663315729098282114555e-1");
yield return new(211.89402314672793, 7.105427357601002e-15, 2147483647, "1.00000000249412202650732834610980320594256417047400774142914274190118361856052595085236408284360973222648923505954459702e+0");
yield return new(3845.8509267737836, 1.1368683772161603e-13, 2147483647, "1.00000000384391759732620859812337468689827732807422971207704645551725241212328716285635291521643450282589991322548812538e+0");
yield return new(7.194211435727692e+43, -2.4758800785707605e+27, -2147483648, "9.99999952975458763643386305058877486036271472333829400321552162516612299438695772346592103090439829465002853181773642631e-1");
yield return new(-8.511077515644868e+234, 3.530017448385272e+218, 2147483647, "-1.00000025189777309831131009623824123776775550295700902051108260673320291667364766634010017385802245185030999231074593344e+0");
yield return new(2.340661158076484e-166, 0.0, 127, "4.96388330698122356918647136417083074020060521165476013617866219817106177365125157388922277565202745752053695364810163365e-2");
yield return new(4.1275614106280375e-271, -1.3134517764154804e-287, 4, "2.53468144652280153556564611352264072257884133969229693013950444296212067942472990824642268732420375796345295708964932872e-68");
yield return new(-9.213242775985736e-17, -0.0, -639047811, "-1.00000005777862803187000272965251737775232550995641263281601383372607441597676684696685792365793705604581986807174244908e+0");
yield return new(-2.8177282979766444e-179, -0.0, 65537, "-9.93746434039797726984894795713540288393487399471650024864191895435377419145563061809435828145413282412517879292791262550e-1");
yield return new(7.760878248329169, -2.220446049250313e-16, -2147483648, "9.99999999045815550189167034707965663409053897698279007700498639296358980422573380113993812792497680208514920153218926296e-1");
yield return new(1.8419099676959588e+174, 0.0, -4, "2.71445658827725156181076683334444614481310154460521810740678887487181766908905713199541635980218473584787818838214263006e-44");
yield return new(-4.58057917440228e-98, -1.6242827758820155e-114, 127, "-1.71218447286765365790130497482630913052641396082053156274452170324892729526321035852729208455618253989588853024865708890e-1");
yield return new(5.530440212706587e+165, 2.0458691299350887e+149, 719523440, "1.00000053040232986901483575057711851457513670998666378008926078179870444432128630874341502190163778421727886219376473113e+0");
yield return new(-7.447226329194892e+122, -0.0, -65537, "-9.95692304941108527193994638909786165038801340972425596567932720287173772731456072607886472076620104037162386218316014717e-1");
yield return new(1.2874265305647036e+282, 6.448907850777164e+265, -1989476957, "9.99999673491299720756272177699742024123053449392723140115069438158817979406378411321796237705169801046516661605404838861e-1");
yield return new(8.130944145689043e-189, 3.986877980439027e-205, 1752848613, "9.99999752920560959020900176252112960242209047173598898749713649167774053647839881849651271885455936856117383063344704400e-1");
yield return new(3.876183036820604e+214, -1.1960164332659527e+198, 2147483646, "1.00000023008702235336384382383614715214062478898779417654927956135141492163277461711548319623347540883688732271471319614e+0");
yield return new(2.3641941970577807e+134, -1.0086913586276987e+118, 2147483646, "1.00000014407879779239530775616128802042207490750772367715257157529727765365034575704539634159047113973472205977586206209e+0");
yield return new(1.9936857683827697e+157, 7.621456421669903e+140, -4, "4.73244769673434352383694491221270547002175645583108454523996488948364213532651761498900807162529157525174755421604992108e-40");
yield return new(1.9084940105219224e+251, -6.359114106063704e+234, 1000000000, "1.00000057859534018260592056053976143664903654482007171833995932261407873448614467658931085729626058463392189189485771106e+0");
yield return new(2.7949104333320924e-117, 0.0, -4, "1.37533351497915778437600229137150021298713951320621732125280688878540514344889084092761491451204936030572159631438592438e+29");
yield return new(2.195423021185122e-306, 0.0, 188663626, "9.99996269533423749853867589661341375595492223304861156828649008665259861671991360119557481840037697819901725171015581450e-1");
yield return new(-1.3476092412136668e+94, -4.631683569492648e+77, -65537, "-9.96698302844303395351717468126818581523166928407903886345435693244846133223521568222471555440850787346461086087787139439e-1");
yield return new(1.8657452817557343e+23, 8388608.0, -418208531, "9.99999871874650270253390180030108539493406134433687301956381164274040243484286936855223710751807717621276257771838093727e-1");
yield return new(3.0577532342784968e+57, -8.711228593176025e+40, -1340787680, "9.99999901278161971090596420772026770098283785227452211510197363850356005305608601020250792584323142174244864962667657797e-1");
yield return new(1.200446821794621e-191, 3.8934355277724873e-208, 1000000000, "9.99999560389037706048535894703202705167720037375994927440591354086581535097938045433773608495441210689837138303311476253e-1");
yield return new(407298368777235.6, 0.0, 2147483646, "1.00000001566510993076042694191285932805091574774807710218272935884494883557926583788156121769964023426355182984760245330e+0");
yield return new(2.553152947715566e-256, -7.394076163542342e-273, -1000000000, "1.00000058852462794154661513094287176579695358607723506900030111985549473747010193929024394381202850185229851179349631104e+0");
yield return new(-8.089804228406667e-96, -0.0, -127, "-5.60733358752204256505303564370453421006744321857002992231614645467680865099542076304144316652330013155324183886348359710e+0");
yield return new(1.5848183572932293e-221, 6.142758149716505e-238, 4, "6.30949896244072396113027807326015467716157454837279787415516116381525930567061185697721080732531988657736085607783192420e-56");
yield return new(-5.400811906966802e-165, 2.409919865102884e-181, 65537, "-9.94245226339258584622472338249494929517881175825041035140083444388302426426673246620758209380365419777419693221540892761e-1");
yield return new(1.2467239335212755e-194, 3.80218313259032e-211, 1000000000, "9.99999553519110889459914545781680656587977473793294069157520601453343753010644623624163888707011803016287684726711554573e-1");
yield return new(-0.0001948344460245681, 6.776263578034403e-21, 127, "-9.34942214783034135539245070771771093859563789033835855877426324483608824490562600749535782057442886608439593908885790348e-1");
yield return new(-6.416747067394642e-66, -2.635549485807631e-82, -65537, "-1.00229311306113946815089917251601295418224344456637035651219084135914843239201102259668875578560015258615740089330510839e+0");
yield return new(-1.9044882322677707e+50, -0.0, 127, "-2.48830588153866380614487162514486985507024178399639839808774218882641263195653644147887825640478553900719541565908138514e+0");
yield return new(2.090698705146643e+95, -7.410693711188237e+78, 2147483646, "1.00000010220477989583480519579838471403236714515717529108154991815187557929846398836908271262964710242349631301026077396e+0");
yield return new(1.197469438278476e+216, -3.827252586451049e+199, -2147483648, "9.99999768315566247587557829846422278270850487965058812317251200309548908719951952268107946717915207682664369244347218050e-1");
yield return new(1.4325686162863053e-198, 4.641336831775293e-215, 127, "2.76805131703498506950946442003834590959556969649772371562393365770074789605935809748874669745913611398186747568718219748e-2");
yield return new(8.278146179900204e+28, 4398046511104.0, -1000000000, "9.99999933414000561001114382792928588119982748717931721884590292180884808272693121431301018425984839616562010808691145327e-1");
yield return new(-6.46953423188362e-287, 2.916448807822559e-303, 65537, "-9.89995378237226425736836362869430740957609347565097513012226701232437043165635327221365866022852771414913372479087224388e-1");
yield return new(2.3203253413093892e+232, 6.894565328877484e+215, -65537, "9.91869274580625281355601787002286264911499679547846224482940533268157589486848769844758178431887459072571833648488110041e-1");
yield return new(6.942208929397988e+283, -2.0636505122486924e+267, -1000000000, "9.99999346431012245575047758202155308987669710247702466994199995434672356021744010985371956830033807612252779129972505001e-1");
yield return new(1.5657194360141887e+74, 6.277101735386681e+57, 4, "3.53735368848383592943287999138846407139876094526073947555242245136981989528797475587989021130057883731043718973909766738e+18");
yield return new(1.390358791636951e+138, -4.131599804939054e+121, 65537, "1.00486533526076262818537314310295266061745925769196282883113448968241314578756017565713525728200833312599088720706550773e+0");
yield return new(9.348734100869121e-52, 0.0, -1668134332, "1.00000007043748562451548319528373646253405802865824138390576584077472469081147391600624650441760905785682143075705476529e+0");
yield return new(5.178613773208296e-92, 0.0, 65537, "9.96797891566569842800049585877877528127936415829353028192957166154442160635270768183594779549246721749721632293408430855e-1");
yield return new(5.0698267559551676e+151, 1.4536774485912138e+135, -2147483648, "9.99999837338166647266350174723685949013840814786201550832745996724358377917411268025669096148626418410905344600615923294e-1");
yield return new(1.034081095065623e-251, 4.845781754539109e-268, -1559378052, "1.00000037060637828545488805849060888397685461087262205738101272004964085330467362025703567192367910711932923038183371924e+0");
yield return new(5.791360190131223e-307, 0.0, 2147483646, "9.99999671644931075345309086648411894880937948221630629423613276787165385319606396272193015123081378100976754939511822170e-1");
yield return new(3.1912726020696516e+93, 1.157920892373162e+77, -65537, "9.96720210424809643195460585585345032365924291220844697285519347350438099114791572893450810018343537673375717597774521975e-1");
yield return new(8.03058474954203e-111, 3.6931914471142943e-127, 2147483647, "9.99999881953153232202262291518000434956020453260694517983299137824431868487737164388099237580353011145056661838951380078e-1");
yield return new(2.5716376285935388e-306, -8e-323, -1566221104, "1.00000044926393330741218605535007621690756604827093176375016511671389558134278666076860075230766034954906607033670027553e+0");
yield return new(1.8398707077556116e-147, -6.946121092140867e-164, 2147483646, "9.99999842666887164084108667095974690029187671198470502507407582564312410787533069805456056930069491986671976754661608315e-1");
yield return new(-3.30509212760339e+249, 9.936115790724537e+232, -2147483647, "-9.99999732459394300862374803801064442288845757057651033966190540721139526893177694863892140496876295874925340942670874971e-1");
yield return new(2.6075806986752073e+116, 0.0, -65537, "9.95918170855849105018928408920364369751082373624741073813007896568306115498669086958253119187756514513692368147968431238e-1");
yield return new(1.7194682538687896e-213, 0.0, -1000000000, "1.00000048990872972467452708796197761722411163691124717656309940040393025231577669792505649185211633418813178677598852976e+0");
yield return new(9.283304623938825e+38, -3.777893186295716e+22, 2147483647, "1.00000004178213562591174618971926870964597389286142791370619443871801932482348571453127807926668605591142723233402126928e+0");
yield return new(-29.863764427512745, -8.881784197001252e-16, 1860190475, "-1.00000000182596669650524853637025515251383605308449188703748631144884343004677391701001858671287174270746932945573042305e+0");
yield return new(-4.2676029742400253e+254, -0.0, 2147483647, "-1.00000027302081987069667460513200540345897074176718889183120969832862103453902965595415356330303052580713689487373980706e+0");
yield return new(-1.2495399091493833e-207, 4.322581767826614e-224, 2147483647, "-9.99999778153241511682335912169117627921421238241223276516635694621422792380317611836323676103160771569618145541946452282e-1");
yield return new(-3.237099341581014e+110, 1.668739871813211e+94, 65537, "-1.00389022489656233489139161409503494171714439670952589297443836449608606855458658142809198309820497341024317818424378928e+0");
yield return new(5.928034092430919e-273, 2.0522684006491881e-289, 127, "7.18587423017942264667593353946128371685538993537989697926984841146193472263548979008216163052791208446408060022659906783e-3");
yield return new(-7.002249664452283e-219, 3.1450921726548502e-235, 127, "-1.91534038242071261078453423201789649388251780126961812394298790734722238205110405605317613811759588670119806126595506850e-2");
yield return new(-1.7043622022420916e+249, -4.968057895362269e+232, -65537, "-9.91281694140230336016766475373925084549157933431974614837124920047225953951838180039301249910233622531316562779569122227e-1");
yield return new(9.527094080692736e+49, 5.192296858534828e+33, 4, "3.12420924700163130594830839437242830109011892848502568546609912734780333951782227893326527258375302229190864203112869753e+12");
yield return new(6.507489929274241e+227, 0.0, 1000000000, "1.00000052455990750147713999080233311815420762808590177472242433609731429934097738171224173744953605172833282978719789162e+0");
yield return new(5.290346928108257e+107, 1.629628781067589e+91, 4, "8.52846827442236959278408089678801198216809550226192670261387678476545698832914470223015974175819887132964020315307614232e+26");
yield return new(4.50467536892071e-142, -1.8208839675781755e-158, -1000000000, "1.00000032546202033961547851786351136311568918394038176183362621822125204117251117181989607384880539227660611939373768935e+0");
yield return new(3.40442595555678e-226, -1.8746210173695387e-242, -375217534, "1.00000138362274360042592849450718126563831971523761345371255819697510140963585300541560779166137330903010476958901622325e+0");
yield return new(9.406402187281749e-30, 0.0, 2147483646, "9.99999968876987103932087248329640714805889435499902178097563345622217774551073024598157198647600848378694897257622459998e-1");
yield return new(-2.2606664499036677, 1.1102230246251565e-16, -127, "-9.93598063278491865561122265063379829983457588848534382350968318906475351215983574612945039749645084105116543746115511511e-1");
yield return new(2.9686257747532166e-141, -1.4567071740625404e-157, -2147483648, "1.00000015067701383786785310618039391509027487717112356123331845038729823168524216734973205672624769847329408634160970425e+0");
yield return new(2.4429246575911563e+33, -7.205759403792794e+16, 2147483646, "1.00000003579934382260962854602507341817773082725192995839888339488982888465350593773249573921291793241774135897054745669e+0");
yield return new(-3.3869862816314936e+144, -0.0, -2147483647, "-9.99999845031570511173808423646736533527316810988792722911256156906815858030627091821281129293493629186943253272071814757e-1");
yield return new(2.082433985866284, 1.1102230246251565e-16, 2147483646, "1.00000000034157996806357694227518406697945620653291173191992400226258605740920621716491602041286186672114263155868134766e+0");
yield return new(-8.00074107062234e-176, -0.0, 65537, "-9.93867008882567484926971157172212439008323279078747059999968037201514544545603197811237076607713953168366487940419542216e-1");
yield return new(4.782055213399832e+292, 2.2158278651204453e+276, -1000000000, "9.99999326080509514521567917867658467649955336350602060763736581071289825463095097877326778914143580546988380293379203968e-1");
yield return new(-3.911094773942849e-13, -1.262177448353619e-29, 65537, "-9.99564161275031603674208440138132132076911199556349768541916174188626619198242301699097125515433399419061397353997422175e-1");
yield return new(3.2323184998191025e-111, -9.232978617785736e-128, -127, "7.41316771373334974704707310475452764651308798187991236053535121857379252737856087405404581704639907841483598231517519659e+0");
yield return new(8.50733799716881e+216, -3.061802069160839e+200, 65537, "1.00765075691389445388761716901924128856465626346358997109444725431682550419542628802718773813774625907651326027945144844e+0");
yield return new(6.822720636297485e+151, 2.9073548971824276e+135, 65537, "1.00534880717635509911862344432239101620302447974752476017083008952674209041158885703341742338508216200644353869910555851e+0");
yield return new(2.5148010091452363e-228, -7.322738349099761e-245, -4, "7.94097984083363964717434593833912174414208055701225727478690372691207652687552968588395552870305134854005140436009252606e+56");
yield return new(2.274366904916594e+134, 1.0086913586276987e+118, 65537, "1.00473166977755143042050199564225048751743245547219670260107760131511187974443269675486987825119112769677476931330407588e+0");
yield return new(4.66102099783489e+296, 0.0, -65537, "9.89630945934300978628014785899623091488939794853343967141108052043453068342812804080815561476876457566745422538392837453e-1");
yield return new(7.329403297733799e-287, 0.0, -774361326, "1.00000085083059515686647088014915734346069336113719564137852986414650769396406897862663074933687793623968704541567337352e+0");
yield return new(3.002155134780217e-286, -1.1665795231290236e-302, 1000000000, "9.99999342560209926354471914398470166430817976965411937438270987643086458881075142419081024445689360682949830853620658847e-1");
yield return new(5.148517738656379e-173, 0.0, 4, "8.47072442738447554572844895348745109337898464461978416360865048353969629348121630200665482065079070543083904545408049187e-44");
yield return new(1.7596423883680487e-28, -5.605193857299268e-45, -2147483647, "1.00000002975914300630241196097170309794556641781906136435313725880986645126229497601116652338236448216630026420892746694e+0");
yield return new(408988352478.6724, 1.52587890625e-05, -1000000000, "9.99999973263047842895861730292735288694519402567871481600124646936313227671046590712048975430310698245174537675230140831e-1");
yield return new(1.5622940179640235e+197, -8.299031137761986e+180, 65537, "1.00695228584730754963423025032266621731542753968072748036732547389997298873817246098518855776065322203775911376176868856e+0");
yield return new(2.945877775190306e+282, 0.0, -372999610, "9.99998256274755929921801031270711978088318789491278345501794105149834796972419091036724814094860185027214804184276294552e-1");
yield return new(5.490742494230184e-34, 0.0, -178423553, "1.00000042923058541965400877781486081135730222137682140392083768831054953099968966791732827505043492889345728277931475002e+0");
yield return new(-0.012189473504087868, 4.336808689942018e-19, -65537, "-1.00006724950357789197825048507090927566078556528650583654352033842105064398061203275492892572405532556513218247285634302e+0");
yield return new(3.492031375473187e+156, 1.9053641054174757e+140, -2147483648, "9.99999832150653060990722311513683255874288082127195082020162678364589548681943322383725064312202742527559819702420679717e-1");
yield return new(8.54395894740883e+245, -2.4258095192198577e+229, -4, "3.28916160305874816795257876986270287439992256944275372029294363630216921625254532288551531840797217278759837285332701527e-62");
yield return new(4.730269133184354e-141, 0.0, -127, "1.27327484345976243951250303325270531714709638688114769719191343218565399429490239331108496755994285419222452843455982501e+1");
yield return new(7.247927092725443e+37, -2.3611832414348226e+21, 2147483646, "1.00000004059465872177657209530344287933141751217669601847096909386853787771407668347260438464880057870596684634113373114e+0");
yield return new(2.983386071693106e-147, -1.3892242184281734e-163, -127, "1.42476239933536814162987280140427439820415853972452097683300979398773801510291431809818527031686311479471639200765823634e+1");
yield return new(-8.434229609096888e+281, -0.0, 1161283319, "-1.00000055900130513350607153711111153207696797022597897669175821208187429708923725633237332223728994507033679999659930981e+0");
yield return new(2.1249966937065933e+252, 1.0174582569701926e+236, -1000000000, "9.99999418994955102524571670681225162129377215045179611381410261019296346666622783273710633160704344321857317321109210464e-1");
yield return new(2.3562951965630124e+66, 0.0, -65537, "9.97670786680017019551761514287146332833852335069843086019124138245078225906369328029580956078906592959565200183521446878e-1");
yield return new(2.19934054611654e+147, -8.872543021186608e+130, -1697275144, "9.99999800110115602063608175827920269677445595886600513702700293196185420393623240505818150691057879982767568894559758724e-1");
yield return new(3.723612085826178e-28, 0.0, 628590675, "9.99999899524940864012837431207602901705446337688745154126451943513207111373762585068307216910833396591650368381146332837e-1");
yield return new(8.27429963953444e-97, 2.598852441411225e-113, 1454096851, "9.99999847852237107936935278161046793789919468972115951458087277505614246262168703107872870744504752604597236794299111142e-1");
yield return new(8.80127389806102e-285, 0.0, 2147483646, "9.99999695428760705480824162446971926882079327342011684595607301211741817420396861015126524410192250400014881213971769219e-1");
yield return new(2.37e-321, 0.0, -4, "1.43299121000516229210780300997594144222621650467393763902519451332474365775140734553999063518699325312875686956828218043e+80");
yield return new(1.397172630062729e+22, -524288.0, 2147483647, "1.00000002374468525854188561540049639103404663000432796644410385472727873708370934772620937274178319124206536963284272180e+0");
yield return new(3.4135408696738415e+145, 0.0, 595303896, "1.00000056291028024992293105425216089589013983141845639009895653925536046152845580605000269221884477576820321279790716094e+0");
yield return new(4.5885623477675914e-79, -1.4981364335015035e-95, 51672230, "9.99996509143484927274438061314275514298012607698048581312578365779740057455897762569048130956160931454225970790205647115e-1");
yield return new(-7.940780653230948e-249, 2.481040258324024e-265, -127, "-8.98554418811462828003467444384223268701071872180950009986048869892767579001670397242684562888725012463339591898961615167e+1");
yield return new(4.751340980299204e+96, 2.3714219875802357e+80, -127, "1.73286698206959275279726305600342072765724502774477119626882651426453042362863192067891586952126938124347056404659568047e-1");
yield return new(4.040267235518782e-231, 0.0, 1683086412, "9.99999684804797747503627700550309310151217931250328641481546176109918819087942658423850411108452012974025503634311011481e-1");
}
}
@@ -0,0 +1,93 @@
"""Generate independent RootN fixtures using only Python's standard library.
Run: python3 path/to/generate_rootn.py [--check]. Exact binary64 input sums
are formed at 2200 digits and verified with Fraction. Decimal ln/exp at 450
and 650 digits must agree after rounding to 120 significant decimal digits.
Sparse corrections below that reference precision have separate exact tests.
"""
from decimal import Decimal, localcontext
from fractions import Fraction
from pathlib import Path
import argparse
import math
import random
import sys
OUTPUT = Path(__file__).with_name('RootNReferenceData.cs')
def reference(high, low, degree, precision):
with localcontext() as context:
context.prec = 2200
value = Decimal.from_float(high) + Decimal.from_float(low)
assert Fraction(value) == Fraction(high) + Fraction(low)
negative = value < 0
assert not negative or degree % 2
with localcontext() as context:
context.prec = precision
result = (abs(value).ln() / degree).exp()
if negative:
result = -result
with localcontext() as rounded_context:
rounded_context.prec = 120
rounded = +result
assert abs(result - rounded) <= abs(rounded) * Decimal(2) ** -350
return format(rounded, 'e')
def inputs():
degrees = [-2147483648, -2147483647, -1000000000, -65537, -127, -4,
4, 127, 65537, 1000000000, 2147483646, 2147483647]
for high in [math.ulp(0.0), 3 * math.ulp(0.0), sys.float_info.min,
math.nextafter(sys.float_info.min, 0.0), 1e-308, 0.5,
math.nextafter(1.0, 0.0), 1.0, math.nextafter(1.0, math.inf),
2.0, 81.0, 1e308, sys.float_info.max]:
for low in [0.0, math.ulp(high) / 4, -math.ulp(high) / 4]:
for degree in degrees:
yield high, low, degree
rng = random.Random(20260916)
for _ in range(192):
high = math.ldexp(rng.uniform(1.0, 1.999), rng.randint(-1074, 1023))
low = rng.choice([-1, 0, 1]) * math.ulp(high) / 4
degree = rng.choice(degrees + [rng.randint(4, 2147483647), -rng.randint(4, 2147483648)])
if degree % 2 and rng.randrange(2):
high, low = -high, -low
yield high, low, degree
def generate():
rows = []
for high, low, degree in dict.fromkeys(inputs()):
first = reference(high, low, degree, 450)
assert first == reference(high, low, degree, 650), (high, low, degree)
rows.append(f' yield return new({high!r}, {low!r}, {degree}, "{first}");')
content = '''// 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
{
internal static IEnumerable<TheoryDataRow<double, double, int, string>> Cases()
{
''' + '\n'.join(rows) + '\n }\n}\n'
return content, len(rows)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--check', action='store_true')
args = parser.parse_args()
content, count = generate()
if args.check:
assert OUTPUT.read_text() == content, 'Fixture is stale; regenerate it'
print(f'Verified {count} RootN references at 450/650 digits with exact input sums.')
else:
OUTPUT.write_text(content)
print(f'Generated {count} RootN references in {OUTPUT.name}.')
if __name__ == '__main__':
main()
+57 -5
View File
@@ -90,6 +90,12 @@ constants. The natural logarithm function is provided separately by `DDMath.Log`
## Mathematical functions ## Mathematical functions
Root functions are implemented on `DoubleDouble` in `DoubleDouble.RootFunctions.cs`:
`Sqrt`, `Cbrt`, `Hypot`, and `RootN` complete `IRootFunctions<DoubleDouble>`.
The additional `InvSqrt` helper and three-argument `Hypot` overload live alongside
them. `DDMath` exposes thin forwarding wrappers; the other mathematical kernels
remain in `DDMath`.
The `DDMath` static class provides: The `DDMath` static class provides:
- `Abs(DoubleDouble)`: preserves both components, maps either signed zero to - `Abs(DoubleDouble)`: preserves both components, maps either signed zero to
@@ -112,6 +118,34 @@ The `DDMath` static class provides:
with FMA. Signed zeros map to correspondingly signed infinities; positive infinity with FMA. Signed zeros map to correspondingly signed infinities; positive infinity
maps to positive zero; negative nonzero inputs and NaN return canonical NaN. maps to positive zero; negative nonzero inputs and NaN return canonical NaN.
This is a dedicated algorithm, not a claim of measured speedup over `1.0 / Sqrt(x)`. This is a dedicated algorithm, not a claim of measured speedup over `1.0 / Sqrt(x)`.
- `Cbrt(DoubleDouble)`: real cube root using a scaled binary64 seed and compensated
Newton corrections. Signed zeros and infinities are preserved; NaN is canonical.
An exponent-tracked low-component correction avoids losing representable sparse
residuals during scaling.
- `Hypot(DoubleDouble, DoubleDouble)`: computes `sqrt(x²+y²)` with bounded scaled
squares and complete-expansion rescaling at exponent boundaries. Widely separated
operands use a small correction without squaring an underflow-prone ratio.
Results are nonnegative, including positive zero. Either infinity takes precedence
over NaN; otherwise NaN propagates. At the upper boundary, exact squared-input
comparisons determine overflow; an overflowing approximation of a finite root
is clamped to the largest finite component pair. Overflow produces positive infinity.
- `Hypot(DoubleDouble, DoubleDouble, DoubleDouble)`: the 3D Euclidean norm
`sqrt(x²+y²+z²)`, with the same special-value and approximate-accuracy contracts.
Coordinates are ordered by magnitude before evaluating scaled squares, so signs
and permutations give identical component bits. Widely separated coordinates
contribute combined corrections without prematurely underflowing their squares.
A zero coordinate reduces to the two-argument overload; exact overflow checks
include all three original inputs. This overload is a convenience API, not an
additional member of `IRootFunctions`.
- `RootN(DoubleDouble, int)`: real nth root for positive degrees, reciprocal root
for negative degrees, supporting the complete `int` range. Degree zero always
returns NaN; negative nonzero inputs require an odd degree. Odd degrees retain
the input sign, while even degrees map either signed zero to positive zero for
positive degrees and positive infinity for negative degrees. Negative degrees
exchange zero and infinity. Unlike `Sqrt(-0)`, `RootN(-0, 2)` is positive zero.
Degrees ±1, ±2 and ±3 reuse identity/reciprocal and existing root kernels; other
degrees refine a binary64 seed using integer powers with separately tracked
exponents, then incorporate the input low without premature underflow.
- `Pow(DoubleDouble, int)`: exponentiation by squaring with a separately tracked - `Pow(DoubleDouble, int)`: exponentiation by squaring with a separately tracked
binary exponent. Supports the full `int` domain, including `int.MinValue`, and binary exponent. Supports the full `int` domain, including `int.MinValue`, and
reciprocates a bounded significand before final scaling for negative exponents. reciprocates a bounded significand before final scaling for negative exponents.
@@ -138,8 +172,12 @@ The `DDMath` static class provides:
```csharp ```csharp
using Just.PreciseMath; using Just.PreciseMath;
DoubleDouble root = DDMath.Sqrt(new DoubleDouble(2.0)); DoubleDouble root = DoubleDouble.Sqrt(new DoubleDouble(2.0));
DoubleDouble inverseRoot = DDMath.InvSqrt(new DoubleDouble(2.0)); DoubleDouble inverseRoot = DoubleDouble.InvSqrt(new DoubleDouble(2.0));
DoubleDouble cubeRoot = DoubleDouble.Cbrt(new DoubleDouble(-8.0));
DoubleDouble distance = DoubleDouble.Hypot(new DoubleDouble(3.0), new DoubleDouble(4.0));
DoubleDouble norm3D = DoubleDouble.Hypot(new DoubleDouble(2.0), new DoubleDouble(3.0), new DoubleDouble(6.0));
DoubleDouble fifthRoot = DoubleDouble.RootN(new DoubleDouble(2.0), 5);
DoubleDouble reciprocal = DDMath.Reciprocal(new DoubleDouble(3.0)); DoubleDouble reciprocal = DDMath.Reciprocal(new DoubleDouble(3.0));
DoubleDouble magnitude = DDMath.Abs(-root); DoubleDouble magnitude = DDMath.Abs(-root);
DoubleDouble smallPower = DDMath.Pow(new DoubleDouble(2.0), -1024); DoubleDouble smallPower = DDMath.Pow(new DoubleDouble(2.0), -1024);
@@ -165,6 +203,20 @@ both suites exercise half-ulp low-component normalization boundaries.
This is a tested approximate-accuracy contract, not exhaustive coverage This is a tested approximate-accuracy contract, not exhaustive coverage
of all component pairs or a guarantee of correctly rounded results. of all component pairs or a guarantee of correctly rounded results.
Cube-root and nth-root tests check `2^-100` relative error for finite nonzero
inputs with degree magnitude at least two. Small degrees use exact integer-power
inequalities; large degrees use independently generated decimal references checked
at 450 and 650 digits, including `int.MinValue` and `int.MaxValue`.
`RootN(x, -1)` retains the reciprocal contract, including its subnormal error floor.
Hypotenuse tests use exact squared inequalities with `2^-100` relative error plus
one minimum binary64 subnormal. Tests also check exact binary cases, sparse lows,
special-value signs, facade component-bit agreement, and constrained generic dispatch.
The 2D and 3D hypotenuse overflow tests bracket the exact threshold, including scaled
55/48/73 and 1/2/2/3 Pythagorean cases at the midpoint. Three-coordinate tests include
all signs and permutations of boundary cases and sparse corrections, including
corrections that only become representable when combined. Finite components remain approximate,
not universally correctly rounded, including near underflow.
Logarithm tests compare exact component sums with independently generated Logarithm tests compare exact component sums with independently generated
120-digit decimal references, requiring agreement at 450 and 650 digits. They 120-digit decimal references, requiring agreement at 450 and 650 digits. They
check `2^-100` relative error plus one minimum binary64 subnormal, with an explicit check `2^-100` relative error plus one minimum binary64 subnormal, with an explicit
@@ -291,9 +343,9 @@ bool success = DoubleDouble.TryParse("1.25e-2".AsSpan(), CultureInfo.InvariantCu
## Deferred scope ## Deferred scope
Natural `DDMath.Log`, `Exp`, and all three `Pow` overloads are implemented. Natural `DDMath.Log`, `Exp`, and all three `Pow` overloads are implemented.
Logarithms in other bases, generic-math interfaces beyond `ISignedNumber` and Logarithms in other bases, generic-math interfaces beyond `ISignedNumber`,
`IFloatingPointConstants`, additional text formats/general round-trip formatting, `IFloatingPointConstants`, and `IRootFunctions`, additional text formats/general
and non-arithmetic performance benchmarks remain deferred. round-trip formatting, and non-arithmetic performance benchmarks remain deferred.
Replacing allocating arithmetic boundary fallbacks is also deferred; the current Replacing allocating arithmetic boundary fallbacks is also deferred; the current
`BigInteger` paths remain in place. That optimization does not require removing `BigInteger` paths remain in place. That optimization does not require removing
`BigInteger` from conversions, parsing, formatting, or independent test oracles. `BigInteger` from conversions, parsing, formatting, or independent test oracles.