using System.Globalization; using Shouldly; using Xunit; namespace Just.PreciseMath.Tests; public class DoubleDoubleNumberStylesTests { [Theory] [InlineData("123", NumberStyles.None, 123.0)] [InlineData(" -1.25e+2 ", NumberStyles.Float, -125.0)] [InlineData("1,234.5-", NumberStyles.Number, -1234.5)] [InlineData("(¤1,234.5)", NumberStyles.Currency, -1234.5)] [InlineData("1,234.5¤", NumberStyles.Currency, 1234.5)] [InlineData("(1.25E2)", NumberStyles.Any, -125.0)] public void DecimalStylesSupportTheirStandardSyntax(string text, NumberStyles style, double expected) { DoubleDouble.Parse(text, style, CultureInfo.InvariantCulture).ShouldBe(new DoubleDouble(expected)); DoubleDouble.Parse(text.AsSpan(), style, CultureInfo.InvariantCulture).ShouldBe(new DoubleDouble(expected)); } [Theory] [InlineData(" 1", NumberStyles.None)] [InlineData("1 ", NumberStyles.None)] [InlineData("-1", NumberStyles.None)] [InlineData("1-", NumberStyles.Float)] [InlineData("1.0", NumberStyles.Integer)] [InlineData("1e2", NumberStyles.Number)] [InlineData("1,000", NumberStyles.Float)] [InlineData("(1)", NumberStyles.Number)] [InlineData("¤1", NumberStyles.Number)] [InlineData("--1", NumberStyles.Any)] [InlineData("1.2,3", NumberStyles.Any)] [InlineData("(1)-", NumberStyles.Any)] [InlineData("1e+", NumberStyles.Any)] public void DisallowedOrMalformedSyntaxFailsWithZero(string text, NumberStyles style) { DoubleDouble.TryParse(text, style, CultureInfo.InvariantCulture, out DoubleDouble result).ShouldBeFalse(); result.ShouldBe(DoubleDouble.Zero); DoubleDouble.TryParse(text.AsSpan(), style, CultureInfo.InvariantCulture, out result).ShouldBeFalse(); result.ShouldBe(DoubleDouble.Zero); Should.Throw(() => DoubleDouble.Parse(text, style, CultureInfo.InvariantCulture)); Should.Throw(() => DoubleDouble.Parse(text.AsSpan(), style, CultureInfo.InvariantCulture)); } [Theory] [InlineData(NumberStyles.HexNumber)] [InlineData(NumberStyles.BinaryNumber)] [InlineData((NumberStyles)1024)] [InlineData((NumberStyles)(-1))] public void InvalidStylesThrowEvenForNullTryParse(NumberStyles style) { Should.Throw(() => DoubleDouble.Parse("1", style, null)); Should.Throw(() => DoubleDouble.Parse("1".AsSpan(), style, null)); Should.Throw(() => DoubleDouble.TryParse((string?)null, style, null, out _)); Should.Throw(() => DoubleDouble.TryParse(ReadOnlySpan.Empty, style, null, out _)); } [Fact] public void NullAndLengthContractsArePreserved() { Should.Throw(() => DoubleDouble.Parse(null!, NumberStyles.Any, null)); DoubleDouble.TryParse((string?)null, NumberStyles.Any, null, out DoubleDouble result).ShouldBeFalse(); result.ShouldBe(DoubleDouble.Zero); DoubleDouble.TryParse(new string('0', 2049), NumberStyles.Any, null, out result).ShouldBeFalse(); DoubleDouble.Parse(new string('0', 2048), NumberStyles.None, null).ShouldBe(DoubleDouble.Zero); DoubleDouble.TryParse("1,000", CultureInfo.InvariantCulture, out _).ShouldBeFalse(); } [Fact] public void CultureAndExponentPreserveExactComponents() { NumberFormatInfo info = new() { NegativeSign = "minus", PositiveSign = "plus", NumberDecimalSeparator = ";;", NumberGroupSeparator = "_", CurrencyDecimalSeparator = ":", CurrencyGroupSeparator = "~", CurrencySymbol = "USD" }; DoubleDouble.Parse("minus9_007_199_254_740_993;;0Eplus0", NumberStyles.Any, info) .ShouldBe(DoubleDouble.FromComponents(-9007199254740992.0, -1.0)); DoubleDouble.Parse("USD9~007~199~254~740~993:0", NumberStyles.Currency, info) .ShouldBe(DoubleDouble.FromComponents(9007199254740992.0, 1.0)); } [Theory] [InlineData("-0", -0.0)] [InlineData("-1e-9999", -0.0)] [InlineData("1e9999", double.PositiveInfinity)] [InlineData("-Infinity", double.NegativeInfinity)] [InlineData("NaN", double.NaN)] public void NonfiniteAndSignedZeroContractsArePreserved(string text, double expected) { DoubleDouble value = DoubleDouble.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture); BitConverter.DoubleToInt64Bits(value.High).ShouldBe(BitConverter.DoubleToInt64Bits(expected)); value.Low.ShouldBe(0.0); } [Fact] public void StyledParsingRetainsExactIntegerResidualAcrossOverloads() { const string text = " 9,007,199,254,740,993 "; NumberStyles style = NumberStyles.Number; DoubleDouble expected = DoubleDouble.FromComponents(9007199254740992.0, 1.0); DoubleDouble.Parse(text, style, CultureInfo.InvariantCulture).ShouldBe(expected); DoubleDouble.Parse(text.AsSpan(), style, CultureInfo.InvariantCulture).ShouldBe(expected); DoubleDouble.TryParse(text, style, CultureInfo.InvariantCulture, out DoubleDouble fromString).ShouldBeTrue(); fromString.ShouldBe(expected); DoubleDouble.TryParse(text.AsSpan(), style, CultureInfo.InvariantCulture, out DoubleDouble fromSpan).ShouldBeTrue(); fromSpan.ShouldBe(expected); } }