109 lines
4.7 KiB
C#
109 lines
4.7 KiB
C#
using System.Globalization;
|
|
using System.Numerics;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace Just.PreciseMath.Tests;
|
|
|
|
public class DoubleDoubleSpecialValueTests
|
|
{
|
|
[Theory]
|
|
[InlineData("+NaN")]
|
|
[InlineData("-NaN")]
|
|
[InlineData("+Infinity")]
|
|
[InlineData("-Infinity")]
|
|
[InlineData(" NaN ")]
|
|
[InlineData(" \t+Infinity\r\n")]
|
|
public void StandardSpecialValuesMatchBinary64WithoutStyleFlags(string text)
|
|
{
|
|
double.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out double expected).ShouldBeTrue();
|
|
AssertAllParsers(text, CultureInfo.InvariantCulture, expected);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("inf", double.PositiveInfinity)]
|
|
[InlineData("+INF", double.PositiveInfinity)]
|
|
[InlineData("-iNf", double.NegativeInfinity)]
|
|
[InlineData(" \t-inf\r\n", double.NegativeInfinity)]
|
|
public void ShortInfinityAliasIsCaseInsensitive(string text, double expected)
|
|
{
|
|
AssertAllParsers(text, CultureInfo.InvariantCulture, expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShortInfinityUsesCultureSignsAndRespectsCustomSymbolPrecedence()
|
|
{
|
|
NumberFormatInfo info = new() { PositiveSign = "plus", NegativeSign = "minus" };
|
|
AssertAllParsers(" minusINF ", info, double.NegativeInfinity);
|
|
AssertAllParsers("plusinf", info, double.PositiveInfinity);
|
|
info.NaNSymbol = "inf";
|
|
AssertAllParsers("inf", info, double.NaN);
|
|
info.NaNSymbol = "missing";
|
|
info.NegativeInfinitySymbol = "inf";
|
|
AssertAllParsers("inf", info, double.NegativeInfinity);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("in")]
|
|
[InlineData("infx")]
|
|
[InlineData("infinite")]
|
|
[InlineData("--inf")]
|
|
[InlineData("+ inf")]
|
|
[InlineData("inf-")]
|
|
public void MalformedAliasesAreRejected(string text)
|
|
{
|
|
DoubleDouble.TryParse(text, CultureInfo.InvariantCulture, out DoubleDouble result).ShouldBeFalse();
|
|
result.ShouldBe(DoubleDouble.Zero);
|
|
DoubleDouble.TryParse(text.AsSpan(), NumberStyles.Any, CultureInfo.InvariantCulture, out result).ShouldBeFalse();
|
|
result.ShouldBe(DoubleDouble.Zero);
|
|
}
|
|
|
|
[Fact]
|
|
public void SpecialValuesStillRespectRawLengthAndInvalidStyles()
|
|
{
|
|
AssertAllParsers(new string(' ', 2045) + "inf", CultureInfo.InvariantCulture, double.PositiveInfinity);
|
|
string tooLong = new string(' ', 2046) + "inf";
|
|
DoubleDouble.TryParse(tooLong, CultureInfo.InvariantCulture, out _).ShouldBeFalse();
|
|
DoubleDouble.TryParse(tooLong.AsSpan(), NumberStyles.None, CultureInfo.InvariantCulture, out _).ShouldBeFalse();
|
|
Should.Throw<FormatException>(() => DoubleDouble.Parse(tooLong, NumberStyles.None, CultureInfo.InvariantCulture));
|
|
Should.Throw<ArgumentException>(() => DoubleDouble.TryParse("inf", NumberStyles.HexNumber, null, out _));
|
|
DoubleDouble.TryParse(" +1 ", NumberStyles.None, CultureInfo.InvariantCulture, out _).ShouldBeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void AbsCanonicalizesRawNaNPayloadsOfEitherSign()
|
|
{
|
|
foreach (long bits in new[] { 0x7ff8000000000001L, unchecked((long)0xfff8000000000001UL) })
|
|
{
|
|
DoubleDouble result = DoubleDouble.Abs(new DoubleDouble(BitConverter.Int64BitsToDouble(bits), 0.0));
|
|
BitConverter.DoubleToInt64Bits(result.High).ShouldBe(BitConverter.DoubleToInt64Bits(double.NaN));
|
|
BitConverter.DoubleToInt64Bits(result.Low).ShouldBe(0L);
|
|
DoubleDouble.IsCanonical(result).ShouldBeTrue();
|
|
}
|
|
}
|
|
|
|
private static void AssertAllParsers(string text, IFormatProvider provider, double expected)
|
|
{
|
|
DoubleDouble.TryParse(text, provider, out DoubleDouble fromString).ShouldBeTrue();
|
|
DoubleDouble.TryParse(text.AsSpan(), provider, out DoubleDouble fromSpan).ShouldBeTrue();
|
|
DoubleDouble[] values = [fromString, fromSpan, DoubleDouble.Parse(text, provider), DoubleDouble.Parse(text.AsSpan(), provider),
|
|
ParseStyled<DoubleDouble>(text, provider)];
|
|
foreach (DoubleDouble value in values)
|
|
{
|
|
BitConverter.DoubleToInt64Bits(value.High).ShouldBe(BitConverter.DoubleToInt64Bits(expected));
|
|
BitConverter.DoubleToInt64Bits(value.Low).ShouldBe(0L);
|
|
}
|
|
}
|
|
|
|
private static T ParseStyled<T>(string text, IFormatProvider provider) where T : INumberBase<T>
|
|
{
|
|
T.TryParse(text, NumberStyles.None, provider, out T? fromString).ShouldBeTrue();
|
|
T.TryParse(text.AsSpan(), NumberStyles.None, provider, out T? fromSpan).ShouldBeTrue();
|
|
T result = T.Parse(text, NumberStyles.None, provider);
|
|
fromString.ShouldBe(result);
|
|
fromSpan.ShouldBe(result);
|
|
T.Parse(text.AsSpan(), NumberStyles.None, provider).ShouldBe(result);
|
|
return result;
|
|
}
|
|
}
|