This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
using System.Globalization;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Just.PreciseMath.Tests;
|
||||
|
||||
public class DoubleDoubleFormattingTests
|
||||
{
|
||||
[Fact]
|
||||
public void GeneralFormattingTrimsOnlyFractionalZerosAtMaximumPrecision()
|
||||
{
|
||||
NumberFormatInfo provider = new() { NumberDecimalSeparator = "::" };
|
||||
new DoubleDouble(1.25).ToString("G999", provider).ShouldBe("1::25");
|
||||
new DoubleDouble(1000.0).ToString("G999", provider).ShouldBe("1000");
|
||||
new DoubleDouble(-0.0).ToString("G999", provider).ShouldBe("-0");
|
||||
new DoubleDouble(999.5).ToString("G3", provider).ShouldBe("1E+03");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GeneralFormattingRetainsLowComponentDigits()
|
||||
{
|
||||
DoubleDouble value = DoubleDouble.FromComponents(1.0, Math.ScaleB(1.0, -54));
|
||||
value.ToString("G32", CultureInfo.InvariantCulture).ShouldBe("1.0000000000000000555111512312578");
|
||||
value.ToString("G", CultureInfo.InvariantCulture).ShouldBe("1.0000000000000000555111512312578");
|
||||
value.ToString("", CultureInfo.InvariantCulture).ShouldBe("1.0000000000000000555111512312578");
|
||||
((IFormattable)value).ToString(null, CultureInfo.InvariantCulture).ShouldBe("1.0000000000000000555111512312578");
|
||||
new DoubleDouble(100000.0).ToString("G5", CultureInfo.InvariantCulture).ShouldBe("1E+05");
|
||||
new DoubleDouble(0.00001m).ToString("g3", CultureInfo.InvariantCulture).ShouldBe("1e-05");
|
||||
new DoubleDouble(0.0001m).ToString("G3", CultureInfo.InvariantCulture).ShouldBe("0.0001");
|
||||
new DoubleDouble(999.5).ToString("G3", CultureInfo.InvariantCulture).ShouldBe("1E+03");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormattingDoesNotRouteThroughDecimalOrRestrictExponentRange()
|
||||
{
|
||||
// Review-1 §6 verified the former decimal-based formatter threw for 1e100,
|
||||
// printed "0" for 1e-100, threw for NaN/infinity, and emitted for PI digits
|
||||
// already wrong at binary64 precision. Expected digits are exact references:
|
||||
// the PI pair rounded to 32 significant digits, ties to even
|
||||
// (Python Fraction/Decimal at precision 120), and the exact binary64 values of
|
||||
// the powers of ten.
|
||||
DoubleDouble.PI.ToString("G32", CultureInfo.InvariantCulture).ShouldBe("3.1415926535897932384626433832795");
|
||||
new DoubleDouble(1e100).ToString("G", CultureInfo.InvariantCulture).ShouldBe("1.0000000000000000159028911097599E+100");
|
||||
new DoubleDouble(1e-100).ToString("G", CultureInfo.InvariantCulture).ShouldBe("1.0000000000000000199918998026029E-100");
|
||||
// The binary64 values are exactly representable as sums with a zero residual,
|
||||
// so the G32 text must agree with the BCL formatter for the same value.
|
||||
new DoubleDouble(1e100).ToString("G32", CultureInfo.InvariantCulture)
|
||||
.ShouldBe((1e100).ToString("G32", CultureInfo.InvariantCulture));
|
||||
new DoubleDouble(1e-100).ToString("G32", CultureInfo.InvariantCulture)
|
||||
.ShouldBe((1e-100).ToString("G32", CultureInfo.InvariantCulture));
|
||||
DoubleDouble.NaN.ToString(CultureInfo.InvariantCulture).ShouldBe("NaN");
|
||||
new DoubleDouble(double.PositiveInfinity).ToString(CultureInfo.InvariantCulture).ShouldBe("Infinity");
|
||||
new DoubleDouble(double.NegativeInfinity).ToString(CultureInfo.InvariantCulture).ShouldBe("-Infinity");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(2.5, "F0", "2")]
|
||||
[InlineData(3.5, "F0", "4")]
|
||||
[InlineData(-2.5, "F0", "-2")]
|
||||
[InlineData(1.25, "F1", "1.2")]
|
||||
[InlineData(9.5, "E0", "1E+001")]
|
||||
[InlineData(0.125, "e2", "1.25e-001")]
|
||||
[InlineData(0.0, "E2", "0.00E+000")]
|
||||
[InlineData(-0.0, "F2", "-0.00")]
|
||||
public void FixedAndExponentialRoundToEven(double value, string format, string expected)
|
||||
{
|
||||
new DoubleDouble(value).ToString(format, CultureInfo.InvariantCulture).ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormattingCoversFullBinary64Range()
|
||||
{
|
||||
new DoubleDouble(double.MaxValue).ToString("E5", CultureInfo.InvariantCulture).ShouldBe("1.79769E+308");
|
||||
new DoubleDouble(double.Epsilon).ToString("G6", CultureInfo.InvariantCulture).ShouldBe("4.94066E-324");
|
||||
DoubleDouble.FromComponents(1.0, double.Epsilon).ToString("F324", CultureInfo.InvariantCulture)
|
||||
.ShouldBe("1." + new string('0', 323) + "5");
|
||||
DoubleDouble.FromComponents(1.0, Math.ScaleB(1.0, -54)).ToString("F30", CultureInfo.InvariantCulture)
|
||||
.ShouldBe("1.000000000000000055511151231258");
|
||||
DoubleDouble.FromComponents(2.5, double.Epsilon).ToString("F0", CultureInfo.InvariantCulture).ShouldBe("3");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "Tests intentionally exercise the current-culture overloads and derive their separator from CurrentCulture.")]
|
||||
public void FormattingUsesProviderForSeparatorsSignsAndSpecialValues()
|
||||
{
|
||||
NumberFormatInfo provider = new()
|
||||
{
|
||||
NumberDecimalSeparator = ",",
|
||||
NegativeSign = "minus",
|
||||
PositiveSign = "plus",
|
||||
NumberDecimalDigits = 3,
|
||||
NaNSymbol = "not-number",
|
||||
PositiveInfinitySymbol = "infinite",
|
||||
NegativeInfinitySymbol = "minus-infinite"
|
||||
};
|
||||
new DoubleDouble(-1.25).ToString("F", provider).ShouldBe("minus1,250");
|
||||
new DoubleDouble(125.0).ToString("E2", provider).ShouldBe("1,25Eplus002");
|
||||
new DoubleDouble(-1.25).ToString(provider).ShouldBe("minus1,25");
|
||||
DoubleDouble.NaN.ToString("G", provider).ShouldBe("not-number");
|
||||
new DoubleDouble(double.PositiveInfinity).ToString("F2", provider).ShouldBe("infinite");
|
||||
new DoubleDouble(double.NegativeInfinity).ToString("E", provider).ShouldBe("minus-infinite");
|
||||
new DoubleDouble(1.25).ToString("F2", CultureInfo.GetCultureInfo("fr-FR")).ShouldBe("1,25");
|
||||
new DoubleDouble(1.25).ToString().ShouldBe("1" + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator + "25");
|
||||
new DoubleDouble(1.25).ToString("F1").ShouldBe("1" + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator + "2");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("R")]
|
||||
[InlineData("N2")]
|
||||
[InlineData("0.00")]
|
||||
[InlineData("G1000")]
|
||||
[InlineData("F-1")]
|
||||
[InlineData("F 2")]
|
||||
[InlineData("E999999999999999999999")]
|
||||
public void UnsupportedOrUnboundedFormatsThrow(string format)
|
||||
{
|
||||
Should.Throw<FormatException>(() => DoubleDouble.One.ToString(format, CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user