using System; using System.Globalization; using Glass.Text; using UnitTestSharp; namespace Glass.UnitTests.Text { public class NumberFormatTests : TestFixture { public static string CountOf(NumberFormat format, long value) { var buffer = new char[format.MaxCountLength]; int end = format.WriteCount(value, buffer, 0); return new string(buffer, 0, end); } public static string FixedOf(NumberFormat format, double value, int decimals) { var buffer = new char[format.MaxFixedLength]; int end = format.WriteFixed(value, decimals, buffer, 0); return new string(buffer, 0, end); } public static string ScientificOf(NumberFormat format, double value, int digits) { var buffer = new char[format.MaxScientificLength]; int end = format.WriteScientific(value, digits, buffer, 0); return new string(buffer, 0, end); } public static string ShortOf(NumberFormat format, double value, int digits) { var buffer = new char[format.MaxShortLength]; int end = format.WriteShortExponent(value, digits, buffer, 0); return new string(buffer, 0, end); } public static NumberFormat Grouping(string separator, int primarySize, int secondarySize, string minusSign) { return new NumberFormat(separator, primarySize, secondarySize, minusSign, ".", "NaN", "Infinity", "e", ShortNotation.English); } public static CultureInfo CultureWithGroupSizes(string separator, params int[] sizes) { var culture = (CultureInfo)CultureInfo.InvariantCulture.Clone(); culture.NumberFormat.NumberGroupSeparator = separator; culture.NumberFormat.NumberGroupSizes = sizes; return culture; } public class InvariantTests : TestFixture { public void GroupsByThreesWithCommasAndAPlainMinus() { CheckEqual(",", NumberFormat.Invariant.GroupSeparator); CheckEqual(3, NumberFormat.Invariant.PrimaryGroupSize); CheckEqual(3, NumberFormat.Invariant.SecondaryGroupSize); CheckEqual("-", NumberFormat.Invariant.MinusSign); } public void UsesAPeriodAndTheWordsNaNAndInfinity() { CheckEqual(".", NumberFormat.Invariant.DecimalSeparator); CheckEqual("NaN", NumberFormat.Invariant.NotANumberSymbol); CheckEqual("Infinity", NumberFormat.Invariant.InfinitySymbol); } public void UsesTheEnglishShortNotation() { CheckTrue(ReferenceEquals(ShortNotation.English, NumberFormat.Invariant.ShortNotation)); } public void UsesALowercaseEForTheExponent() { CheckEqual("e", NumberFormat.Invariant.ExponentSymbol); } } public class CtorTests : TestFixture { public void KeepsEveryValue() { var format = new NumberFormat(" ", 3, 2, "~", ",", "?", "inf", "e", ShortNotation.English); CheckEqual(" ", format.GroupSeparator); CheckEqual(3, format.PrimaryGroupSize); CheckEqual(2, format.SecondaryGroupSize); CheckEqual("~", format.MinusSign); CheckEqual(",", format.DecimalSeparator); CheckEqual("?", format.NotANumberSymbol); CheckEqual("inf", format.InfinitySymbol); } public void KeepsTheShortNotation() { var notation = new ShortNotation(new[] { "k" }, "", 3); CheckTrue(ReferenceEquals(notation, new NumberFormat(",", 3, 3, "-", ".", "NaN", "Infinity", "e", notation).ShortNotation)); } public void ANullShortNotation_Throws() { CheckThrow(typeof(ArgumentNullException)); new NumberFormat(",", 3, 3, "-", ".", "NaN", "Infinity", "e", null); } public void MaxShortLength_IsNeverBelowTheScientificLength() { CheckEqual(NumberFormat.Invariant.MaxScientificLength, NumberFormat.Invariant.MaxShortLength); } public void MaxShortLength_CoversTheLongestWrittenOutValue() { var format = new NumberFormat(",", 3, 3, "", "", "NaN", "Infinity", "", ShortNotation.English); CheckEqual(1 + 5 + 3 + 15, format.MaxShortLength); } public void MaxShortLength_GrowsWithTheLongestSuffix() { var notation = new ShortNotation(new[] { "K", "AVERYLONGSUFFIXINDEED" }, "", 3); var format = new NumberFormat(",", 3, 3, "-", ".", "NaN", "Infinity", "e", notation); CheckEqual(1 + 15 + 1 + 21, format.MaxShortLength); } public void KeepsTheExponentSymbol() { CheckEqual("x10^", new NumberFormat(",", 3, 3, "-", ".", "NaN", "Infinity", "x10^", ShortNotation.English).ExponentSymbol); } public void MaxScientificLength_CoversTheDigitsSeparatorsSignsAndExponent() { CheckEqual(1 + 1 + 1 + 14 + 1 + 1 + 3, NumberFormat.Invariant.MaxScientificLength); CheckEqual(2 * 5 + 1 + 5 + 14 + 3 + 3, new NumberFormat(",", 3, 3, "MINUS", "", "NaN", "Infinity", "EXP", ShortNotation.English).MaxScientificLength); } public void MaxScientificLength_IsNeverBelowTheSymbolLength() { var format = new NumberFormat(",", 3, 3, "-", ".", new string('N', 100), "Infinity", "e", ShortNotation.English); CheckEqual(100, format.MaxScientificLength); } public void ANullExponentSymbol_Throws() { CheckThrow(typeof(ArgumentNullException)); new NumberFormat(",", 3, 3, "-", ".", "NaN", "Infinity", null, ShortNotation.English); } public void NoPrimaryGroup_MeansNoGroupingAtAll() { CheckEqual(0, Grouping(",", 0, 2, "-").SecondaryGroupSize); } public void MaxCountLength_CoversTheDigitsSeparatorsAndSign() { CheckEqual(19 + 6 + 1, NumberFormat.Invariant.MaxCountLength); CheckEqual(19 + 18 * 3 + 5, Grouping("<->", 1, 1, "MINUS").MaxCountLength); } public void NoGrouping_HasAMaxCountLengthOfDigitsAndSign() { CheckEqual(19 + 1, Grouping(",", 0, 0, "-").MaxCountLength); } public void MaxFixedLength_CoversTwentyNineDigitsTheSeparatorsTheSignAndNineDecimals() { CheckEqual(1 + 29 + 9 + 1 + 9, NumberFormat.Invariant.MaxFixedLength); var longSeparator = new NumberFormat(",", 3, 3, "-", "", "NaN", "Infinity", "e", ShortNotation.English); CheckEqual(1 + 29 + 9 + 5 + 9, longSeparator.MaxFixedLength); } public void MaxFixedLength_IsNeverBelowTheSymbolLength() { var format = new NumberFormat(",", 3, 3, "-", ".", new string('N', 100), "Infinity", "e", ShortNotation.English); CheckEqual(100, format.MaxFixedLength); } public void MaxSymbolLength_IsTheLongerOfNaNAndMinusInfinity() { CheckEqual(9, NumberFormat.Invariant.MaxSymbolLength); CheckEqual(10, new NumberFormat(",", 3, 3, "-", ".", "NotANumber", "inf", "e", ShortNotation.English).MaxSymbolLength); } public void ANullSeparator_Throws() { CheckThrow(typeof(ArgumentNullException)); new NumberFormat(null, 3, 3, "-", ".", "NaN", "Infinity", "e", ShortNotation.English); } public void ANullMinusSign_Throws() { CheckThrow(typeof(ArgumentNullException)); new NumberFormat(",", 3, 3, null, ".", "NaN", "Infinity", "e", ShortNotation.English); } public void ANullDecimalSeparator_Throws() { CheckThrow(typeof(ArgumentNullException)); new NumberFormat(",", 3, 3, "-", null, "NaN", "Infinity", "e", ShortNotation.English); } public void ANullNotANumberSymbol_Throws() { CheckThrow(typeof(ArgumentNullException)); new NumberFormat(",", 3, 3, "-", ".", null, "Infinity", "e", ShortNotation.English); } public void ANullInfinitySymbol_Throws() { CheckThrow(typeof(ArgumentNullException)); new NumberFormat(",", 3, 3, "-", ".", "NaN", null, "e", ShortNotation.English); } public void ANegativePrimaryGroupSize_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Grouping(",", -1, 3, "-"); } public void ANegativeSecondaryGroupSize_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Grouping(",", 3, -1, "-"); } } public class FromCultureTests : TestFixture { public void InvariantCulture_GroupsByThreesWithCommas() { NumberFormat format = NumberFormat.FromCulture(CultureInfo.InvariantCulture); CheckEqual("1,234,567", CountOf(format, 1234567)); } public void GermanCulture_GroupsWithPeriods() { CheckEqual("1.234.567", CountOf(NumberFormat.FromCulture(new CultureInfo("de-DE")), 1234567)); } public void IndianCulture_GroupsByThreeThenTwo() { CheckEqual("12,34,567", CountOf(NumberFormat.FromCulture(new CultureInfo("en-IN")), 1234567)); } public void ACulturesMinusSign_IsUsed() { var culture = (CultureInfo)CultureInfo.InvariantCulture.Clone(); culture.NumberFormat.NegativeSign = "~"; CheckEqual("~5", CountOf(NumberFormat.FromCulture(culture), -5)); } public void AMultiCharacterSeparator_IsUsedWhole() { CheckEqual("1<>234", CountOf(NumberFormat.FromCulture(CultureWithGroupSizes("<>", 3)), 1234)); } public void ASingleSize_RepeatsForEveryGroup() { CheckEqual("1,234,567", CountOf(NumberFormat.FromCulture(CultureWithGroupSizes(",", 3)), 1234567)); } public void ASizeFollowedByZero_MakesOnlyTheFirstGroup() { CheckEqual("1234,567", CountOf(NumberFormat.FromCulture(CultureWithGroupSizes(",", 3, 0)), 1234567)); } public void NoSizes_MeansNoGrouping() { CheckEqual("1234567", CountOf(NumberFormat.FromCulture(CultureWithGroupSizes(",")), 1234567)); } public void AZeroFirstSize_MeansNoGrouping() { CheckEqual("1234567", CountOf(NumberFormat.FromCulture(CultureWithGroupSizes(",", 0)), 1234567)); } public void ACulturesDecimalSeparator_IsUsed() { NumberFormat format = NumberFormat.FromCulture(new CultureInfo("de-DE")); CheckEqual(",", format.DecimalSeparator); CheckEqual("1.234,50", FixedOf(format, 1234.5, 2)); } public void ACulturesNaNAndInfinitySymbols_AreUsed() { var culture = (CultureInfo)CultureInfo.InvariantCulture.Clone(); culture.NumberFormat.NaNSymbol = "nan!"; culture.NumberFormat.PositiveInfinitySymbol = "inf!"; NumberFormat format = NumberFormat.FromCulture(culture); CheckEqual("nan!", format.NotANumberSymbol); CheckEqual("inf!", format.InfinitySymbol); } public void AnyCulture_UsesTheEnglishShortNotation() { CheckTrue(ReferenceEquals(ShortNotation.English, NumberFormat.FromCulture(new CultureInfo("de-DE")).ShortNotation)); } public void AnyCulture_UsesALowercaseEForTheExponent() { CheckEqual("e", NumberFormat.FromCulture(new CultureInfo("de-DE")).ExponentSymbol); } public void ANullCulture_Throws() { CheckThrow(typeof(ArgumentNullException)); NumberFormat.FromCulture(null); } } public class DigitCountTests : TestFixture { public void Zero_HasOneDigit() { CheckEqual(1, NumberFormat.DigitCount(0)); } public void TheEdgesOfEachLength_AreCounted() { CheckEqual(1, NumberFormat.DigitCount(9)); CheckEqual(2, NumberFormat.DigitCount(10)); CheckEqual(2, NumberFormat.DigitCount(99)); CheckEqual(3, NumberFormat.DigitCount(100)); } public void TheLargestMagnitude_HasTwentyDigits() { CheckEqual(20, NumberFormat.DigitCount(ulong.MaxValue)); } } public class MagnitudeOfTests : TestFixture { public void Positive_IsItself() { CheckEqual(42UL, NumberFormat.MagnitudeOf(42)); } public void Negative_DropsTheSign() { CheckEqual(42UL, NumberFormat.MagnitudeOf(-42)); } public void MinValue_IsOneMoreThanMaxValue() { CheckEqual((ulong)long.MaxValue + 1, NumberFormat.MagnitudeOf(long.MinValue)); } } public class WriteCountTests : TestFixture { public void Zero_IsOneDigit() { CheckEqual("0", CountOf(NumberFormat.Invariant, 0)); } public void JustBelowAGroup_HasNoSeparator() { CheckEqual("999", CountOf(NumberFormat.Invariant, 999)); } public void ExactlyOneGroup_GainsASeparator() { CheckEqual("1,000", CountOf(NumberFormat.Invariant, 1000)); } public void ExactlyTheGroupSize_HasNoLeadingSeparator() { CheckEqual("100,000", CountOf(NumberFormat.Invariant, 100000)); CheckEqual("1,000,000", CountOf(NumberFormat.Invariant, 1000000)); } public void Negative_GetsTheMinusSignBeforeTheGroups() { CheckEqual("-1,234", CountOf(NumberFormat.Invariant, -1234)); CheckEqual("-999", CountOf(NumberFormat.Invariant, -999)); } public void Extremes_AreWrittenInFull() { CheckEqual("9,223,372,036,854,775,807", CountOf(NumberFormat.Invariant, long.MaxValue)); CheckEqual("-9,223,372,036,854,775,808", CountOf(NumberFormat.Invariant, long.MinValue)); } public void ASecondarySize_AppliesAfterTheFirstGroup() { var indian = Grouping(",", 3, 2, "-"); CheckEqual("1,000", CountOf(indian, 1000)); CheckEqual("10,000", CountOf(indian, 10000)); CheckEqual("1,00,000", CountOf(indian, 100000)); CheckEqual("1,23,45,67,890", CountOf(indian, 1234567890)); } public void ASecondarySizeOfZero_MakesOnlyTheFirstGroup() { CheckEqual("12345,678", CountOf(Grouping(",", 3, 0, "-"), 12345678)); } public void APrimarySizeOfZero_MakesNoGroups() { CheckEqual("12345678", CountOf(Grouping(",", 0, 3, "-"), 12345678)); } public void GroupsOfOne_SeparateEveryDigit() { CheckEqual("1-2-3", CountOf(Grouping("-", 1, 1, "~"), 123)); } public void AnEmptySeparator_JoinsTheGroups() { CheckEqual("1234", CountOf(Grouping("", 3, 3, "-"), 1234)); } public void AnEmptyMinusSign_WritesNone() { CheckEqual("1,234", CountOf(Grouping(",", 3, 3, ""), -1234)); } public void LongSeparatorsAndMinusSigns_AreWrittenWhole() { var format = Grouping("<->", 3, 3, "MINUS "); CheckEqual("MINUS 12<->345", CountOf(format, -12345)); } public void WritesAtThePositionAndReturnsTheEnd() { var buffer = new char[20]; for (int i = 0; i < buffer.Length; ++i) { buffer[i] = '#'; } int end = NumberFormat.Invariant.WriteCount(-1234, buffer, 3); CheckEqual(9, end); CheckEqual("###-1,234###########", new string(buffer)); } } public class TryRoundFixedTests : TestFixture { public void RoundsHalfAwayFromZero() { CheckTrue(NumberFormat.TryRoundFixed(2.5, 0, out decimal rounded)); CheckEqual(3m, rounded); CheckTrue(NumberFormat.TryRoundFixed(-2.5, 0, out rounded)); CheckEqual(-3m, rounded); } public void ReadsTheValueAsItsFifteenSignificantDigits() { CheckTrue(NumberFormat.TryRoundFixed(1.005, 2, out decimal rounded)); CheckEqual(1.01m, rounded); } public void NaN_CannotBeRounded() { CheckFalse(NumberFormat.TryRoundFixed(double.NaN, 2, out _)); } public void Infinities_CannotBeRounded() { CheckFalse(NumberFormat.TryRoundFixed(double.PositiveInfinity, 2, out _)); CheckFalse(NumberFormat.TryRoundFixed(double.NegativeInfinity, 2, out _)); } public void AValueBeyondWhatTheArithmeticHolds_CannotBeRounded() { CheckFalse(NumberFormat.TryRoundFixed(7.9e28, 2, out _)); CheckFalse(NumberFormat.TryRoundFixed(-1e30, 2, out _)); CheckTrue(NumberFormat.TryRoundFixed(7.8e28, 2, out _)); } public void ADecimalCountOutOfRange_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); NumberFormat.TryRoundFixed(1, 10, out _); } public void ANegativeDecimalCount_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); NumberFormat.TryRoundFixed(1, -1, out _); } } public class WriteFixedTests : TestFixture { public void Decimals_FollowTheDecimalSeparator() { CheckEqual("12.34", FixedOf(NumberFormat.Invariant, 12.34, 2)); } public void ZeroDecimals_WriteNoSeparator() { CheckEqual("1,234", FixedOf(NumberFormat.Invariant, 1234, 0)); } public void TheWholePart_IsGrouped() { CheckEqual("1,234,567.89", FixedOf(NumberFormat.Invariant, 1234567.89, 2)); CheckEqual("12,34,567.89", FixedOf(Grouping(",", 3, 2, "-"), 1234567.89, 2)); } public void TheDecimals_AreNeverGrouped() { CheckEqual("0.123456", FixedOf(NumberFormat.Invariant, 0.123456, 6)); } public void AFractionOnly_HasAZeroWholePart() { CheckEqual("0.05", FixedOf(NumberFormat.Invariant, 0.05, 2)); } public void LeadingZerosOfTheFraction_AreKept() { CheckEqual("1.005", FixedOf(NumberFormat.Invariant, 1.005, 3)); } public void TrailingZerosOfTheFraction_AreKept() { CheckEqual("12.50", FixedOf(NumberFormat.Invariant, 12.5, 2)); } public void Zero_IsZeroWithItsDecimals() { CheckEqual("0.00", FixedOf(NumberFormat.Invariant, 0, 2)); } public void Negative_GetsTheMinusSign() { CheckEqual("-1,234.50", FixedOf(NumberFormat.Invariant, -1234.5, 2)); CheckEqual("-0.05", FixedOf(NumberFormat.Invariant, -0.05, 2)); } public void AValueThatRoundsToZero_HasNoMinusSign() { CheckEqual("0.00", FixedOf(NumberFormat.Invariant, -0.001, 2)); CheckEqual("0.00", FixedOf(NumberFormat.Invariant, -0.0, 2)); } public void RoundsHalfAwayFromZero() { CheckEqual("3", FixedOf(NumberFormat.Invariant, 2.5, 0)); CheckEqual("-3", FixedOf(NumberFormat.Invariant, -2.5, 0)); CheckEqual("1.5", FixedOf(NumberFormat.Invariant, 1.45, 1)); CheckEqual("-1.5", FixedOf(NumberFormat.Invariant, -1.45, 1)); } public void ReadsTheValueAsItsFifteenSignificantDigits() { CheckEqual("1.01", FixedOf(NumberFormat.Invariant, 1.005, 2)); CheckEqual("0.3", FixedOf(NumberFormat.Invariant, 0.1 + 0.2, 1)); } public void RoundingUp_CarriesIntoTheWholePart() { CheckEqual("1,000.00", FixedOf(NumberFormat.Invariant, 999.999, 2)); } public void AValueTooSmallForADecimal_IsZero() { CheckEqual("0.00", FixedOf(NumberFormat.Invariant, 1e-30, 2)); } public void TheDecimalSeparatorAndMinusSign_AreTheFormats() { var format = new NumberFormat(".", 3, 3, "~", ",", "NaN", "Infinity", "e", ShortNotation.English); CheckEqual("~1.234,50", FixedOf(format, -1234.5, 2)); } public void ALongDecimalSeparator_IsWrittenWhole() { var format = new NumberFormat(",", 3, 3, "-", "", "NaN", "Infinity", "e", ShortNotation.English); CheckEqual("150", FixedOf(format, 1.5, 2)); } public void NineDecimals_AreAllWritten() { CheckEqual("0.000000005", FixedOf(NumberFormat.Invariant, 0.000000005, 9)); } public void ALargeValueWithManyDecimals_IsNotLimited() { CheckEqual("10,000,000,000.000000000", FixedOf(NumberFormat.Invariant, 1e10, 9)); } public void AWholePartPastAUlong_IsWrittenInFull() { CheckEqual("10,000,000,000,000,000,000,000,000.00", FixedOf(NumberFormat.Invariant, 1e25, 2)); CheckEqual("-12,345,678,901,234,500,000,000,000.00", FixedOf(NumberFormat.Invariant, -1.23456789012345e25, 2)); } public void AWholePartAtTheQuintillionBoundary_KeepsItsZeros() { CheckEqual("1,000,000,000,000,000,000", FixedOf(NumberFormat.Invariant, 1e18, 0)); CheckEqual("999,999,999,999,999,000", FixedOf(NumberFormat.Invariant, 9.99999999999999e17, 0)); } public void TheLargestValues_AreWrittenInFull() { CheckEqual("78,000,000,000,000,000,000,000,000,000.000000000", FixedOf(NumberFormat.Invariant, 7.8e28, 9)); } public void AValueTooLargeForTheArithmetic_IsAnInfinity() { CheckEqual("Infinity", FixedOf(NumberFormat.Invariant, 7.9e28, 2)); CheckEqual("-Infinity", FixedOf(NumberFormat.Invariant, -1e30, 2)); CheckEqual("Infinity", FixedOf(NumberFormat.Invariant, double.MaxValue, 2)); } public void Infinities_AreTheFormatsSymbol() { CheckEqual("Infinity", FixedOf(NumberFormat.Invariant, double.PositiveInfinity, 2)); CheckEqual("-Infinity", FixedOf(NumberFormat.Invariant, double.NegativeInfinity, 2)); } public void NaN_IsTheFormatsSymbol() { CheckEqual("NaN", FixedOf(NumberFormat.Invariant, double.NaN, 2)); } public void TheLongestOutput_FitsMaxFixedLength() { var format = new NumberFormat("<->", 1, 1, "MINUS", "", "NaN", "Infinity", "e", ShortNotation.English); var buffer = new char[format.MaxFixedLength]; int end = format.WriteFixed(-7.8e28, 9, buffer, 0); CheckEqual("MINUS7<->8<->0<->0<->0<->0<->0<->0<->0<->0<->0<->0<->0<->0<->0<->0<->0<->0<->0<->0" + "<->0<->0<->0<->0<->0<->0<->0<->0<->0000000000", new string(buffer, 0, end)); } public void ADecimalCountOutOfRange_Throws() { var buffer = new char[100]; CheckThrow(typeof(ArgumentOutOfRangeException)); NumberFormat.Invariant.WriteFixed(1, 10, buffer, 0); } public void ANegativeDecimalCount_Throws() { var buffer = new char[100]; CheckThrow(typeof(ArgumentOutOfRangeException)); NumberFormat.Invariant.WriteFixed(1, -1, buffer, 0); } public void WritesAtThePositionAndReturnsTheEnd() { var buffer = new char[10]; for (int i = 0; i < buffer.Length; ++i) { buffer[i] = '#'; } int end = NumberFormat.Invariant.WriteFixed(1.25, 2, buffer, 2); CheckEqual(6, end); CheckEqual("##1.25####", new string(buffer)); } } public class WriteScientificTests : TestFixture { public void AMantissaOfTheDigitsAskedFor_FollowsTheExponent() { CheckEqual("1.23e3", ScientificOf(NumberFormat.Invariant, 1234.5, 3)); CheckEqual("1.2345e3", ScientificOf(NumberFormat.Invariant, 1234.5, 5)); } public void OneDigit_HasNoDecimalSeparator() { CheckEqual("1e0", ScientificOf(NumberFormat.Invariant, 1, 1)); CheckEqual("5e7", ScientificOf(NumberFormat.Invariant, 5e7, 1)); } public void Zero_IsAZeroMantissaAndExponent() { CheckEqual("0.00e0", ScientificOf(NumberFormat.Invariant, 0, 3)); CheckEqual("0e0", ScientificOf(NumberFormat.Invariant, -0.0, 1)); } public void TrailingZerosOfTheMantissa_AreKept() { CheckEqual("1.20e5", ScientificOf(NumberFormat.Invariant, 120000, 3)); CheckEqual("1.00000000000000e-1", ScientificOf(NumberFormat.Invariant, 0.1, 15)); } public void AllFifteenDigits_AreWritten() { CheckEqual("1.23456789000000e8", ScientificOf(NumberFormat.Invariant, 123456789, 15)); } public void Negative_GetsTheMinusSignBeforeTheMantissa() { CheckEqual("-1.5e-7", ScientificOf(NumberFormat.Invariant, -1.5e-7, 2)); CheckEqual("-1.50e3", ScientificOf(NumberFormat.Invariant, -1500, 3)); } public void ANegativeExponent_GetsTheMinusSign() { CheckEqual("1.23e-5", ScientificOf(NumberFormat.Invariant, 0.0000123, 3)); } public void RoundsHalfAwayFromZero() { CheckEqual("1.235e3", ScientificOf(NumberFormat.Invariant, 1234.5, 4)); CheckEqual("3e0", ScientificOf(NumberFormat.Invariant, 2.5, 1)); CheckEqual("-3e0", ScientificOf(NumberFormat.Invariant, -2.5, 1)); CheckEqual("1.01e0", ScientificOf(NumberFormat.Invariant, 1.005, 3)); } public void RoundingUp_CarriesIntoTheExponent() { CheckEqual("1.00e1", ScientificOf(NumberFormat.Invariant, 9.995, 3)); CheckEqual("1.00e6", ScientificOf(NumberFormat.Invariant, 999999.9, 3)); CheckEqual("1.00e0", ScientificOf(NumberFormat.Invariant, 0.9999, 3)); CheckEqual("1e-4", ScientificOf(NumberFormat.Invariant, 0.00009999, 1)); } public void ExactPowersOfTen_HaveAMantissaOfOne() { for (int power = -20; power <= 20; ++power) { double value = double.Parse("1e" + power, CultureInfo.InvariantCulture); CheckEqual("1.00e" + (power < 0 ? "-" : "") + Math.Abs(power), ScientificOf(NumberFormat.Invariant, value, 3)); } } public void TheLargestDoubles_AreWritten() { CheckEqual("1.00e308", ScientificOf(NumberFormat.Invariant, 1e308, 3)); CheckEqual("1.80e308", ScientificOf(NumberFormat.Invariant, double.MaxValue, 3)); CheckEqual("-1.80e308", ScientificOf(NumberFormat.Invariant, -double.MaxValue, 3)); } public void TheSmallestDoubles_AreWritten() { CheckEqual("4.94e-324", ScientificOf(NumberFormat.Invariant, double.Epsilon, 3)); CheckEqual("2.23e-308", ScientificOf(NumberFormat.Invariant, 2.2250738585072014e-308, 3)); CheckEqual("1.00e-310", ScientificOf(NumberFormat.Invariant, 1e-310, 3)); } public void TheFormatsSeparatorsAndSymbols_AreUsed() { var format = new NumberFormat(".", 3, 3, "~", ",", "NaN", "Infinity", " x10^", ShortNotation.English); CheckEqual("~1,23 x10^~3", ScientificOf(format, -0.00123, 3)); CheckEqual("4,5 x10^6", ScientificOf(format, 4500000, 2)); } public void NaN_IsTheFormatsSymbol() { CheckEqual("NaN", ScientificOf(NumberFormat.Invariant, double.NaN, 3)); } public void Infinities_AreTheFormatsSymbol() { CheckEqual("Infinity", ScientificOf(NumberFormat.Invariant, double.PositiveInfinity, 3)); CheckEqual("-Infinity", ScientificOf(NumberFormat.Invariant, double.NegativeInfinity, 3)); } public void TheLongestOutput_FillsMaxScientificLengthExactly() { var format = new NumberFormat(",", 3, 3, "MINUS", "", "NaN", "Infinity", "EXP", ShortNotation.English); var buffer = new char[format.MaxScientificLength]; int end = format.WriteScientific(-1.23456789012345e-300, 15, buffer, 0); CheckEqual(format.MaxScientificLength, end); CheckEqual("MINUS123456789012345EXPMINUS300", new string(buffer, 0, end)); } public void ZeroDigits_Throw() { var buffer = new char[100]; CheckThrow(typeof(ArgumentOutOfRangeException)); NumberFormat.Invariant.WriteScientific(1, 0, buffer, 0); } public void SixteenDigits_Throw() { var buffer = new char[100]; CheckThrow(typeof(ArgumentOutOfRangeException)); NumberFormat.Invariant.WriteScientific(1, 16, buffer, 0); } public void WritesAtThePositionAndReturnsTheEnd() { var buffer = new char[12]; for (int i = 0; i < buffer.Length; ++i) { buffer[i] = '#'; } int end = NumberFormat.Invariant.WriteScientific(1250, 3, buffer, 2); CheckEqual(8, end); CheckEqual("##1.25e3####", new string(buffer)); } } public class WriteShortExponentTests : TestFixture { public void BelowAThousand_HasNoSuffix() { CheckEqual("1.00", ShortOf(NumberFormat.Invariant, 1, 3)); CheckEqual("12.3", ShortOf(NumberFormat.Invariant, 12.3456, 3)); CheckEqual("123", ShortOf(NumberFormat.Invariant, 123.456, 3)); CheckEqual("999", ShortOf(NumberFormat.Invariant, 999, 3)); } public void Zero_IsZeroWithItsDigits() { CheckEqual("0.00", ShortOf(NumberFormat.Invariant, 0, 3)); CheckEqual("0", ShortOf(NumberFormat.Invariant, 0, 1)); CheckEqual("0", ShortOf(NumberFormat.Invariant, -0.0, 1)); } public void EachNamedSuffix_StartsAtItsPower() { CheckEqual("1.00K", ShortOf(NumberFormat.Invariant, 1000, 3)); CheckEqual("1.00M", ShortOf(NumberFormat.Invariant, 1e6, 3)); CheckEqual("1.00B", ShortOf(NumberFormat.Invariant, 1e9, 3)); CheckEqual("1.00T", ShortOf(NumberFormat.Invariant, 1e12, 3)); } public void TheGeneratedSuffixes_FollowTheNamedOnes() { CheckEqual("1.00aa", ShortOf(NumberFormat.Invariant, 1e15, 3)); CheckEqual("1.00ab", ShortOf(NumberFormat.Invariant, 1e18, 3)); CheckEqual("1.00ag", ShortOf(NumberFormat.Invariant, 1e33, 3)); CheckEqual("1.00az", ShortOf(NumberFormat.Invariant, 1e90, 3)); CheckEqual("1.00ba", ShortOf(NumberFormat.Invariant, 1e93, 3)); } public void TheLargestDouble_IsTwoLetters() { CheckEqual("100dt", ShortOf(NumberFormat.Invariant, 1e308, 3)); CheckEqual("180dt", ShortOf(NumberFormat.Invariant, double.MaxValue, 3)); } public void TheMantissa_ShiftsWithinTheGroup() { CheckEqual("1.23K", ShortOf(NumberFormat.Invariant, 1234, 3)); CheckEqual("12.3K", ShortOf(NumberFormat.Invariant, 12345, 3)); CheckEqual("123K", ShortOf(NumberFormat.Invariant, 123456, 3)); CheckEqual("1.23M", ShortOf(NumberFormat.Invariant, 1234567, 3)); CheckEqual("1.50M", ShortOf(NumberFormat.Invariant, 1.5e6, 3)); } public void TheWholePart_IsNeverCutShort() { CheckEqual("123K", ShortOf(NumberFormat.Invariant, 123456, 2)); CheckEqual("123K", ShortOf(NumberFormat.Invariant, 123456, 1)); CheckEqual("12K", ShortOf(NumberFormat.Invariant, 12345, 1)); CheckEqual("2K", ShortOf(NumberFormat.Invariant, 1500, 1)); } public void RoundingUp_CarriesIntoTheNextSuffix() { CheckEqual("1.00K", ShortOf(NumberFormat.Invariant, 999.5, 3)); CheckEqual("1.00M", ShortOf(NumberFormat.Invariant, 999500, 3)); CheckEqual("1.00aa", ShortOf(NumberFormat.Invariant, 999.9e12, 3)); CheckEqual("1.00ba", ShortOf(NumberFormat.Invariant, 9.9999e92, 3)); } public void RoundingUp_RecomputesTheDigitsAfterTheCarry() { CheckEqual("1.0K", ShortOf(NumberFormat.Invariant, 999.6, 2)); CheckEqual("100", ShortOf(NumberFormat.Invariant, 99.96, 2)); CheckEqual("1K", ShortOf(NumberFormat.Invariant, 999.6, 1)); } public void RoundsHalfAwayFromZero() { CheckEqual("13", ShortOf(NumberFormat.Invariant, 12.5, 2)); CheckEqual("-13", ShortOf(NumberFormat.Invariant, -12.5, 2)); CheckEqual("1.24K", ShortOf(NumberFormat.Invariant, 1235, 3)); } public void Negative_GetsTheMinusSign() { CheckEqual("-1.23K", ShortOf(NumberFormat.Invariant, -1234, 3)); CheckEqual("-1.00K", ShortOf(NumberFormat.Invariant, -999.5, 3)); CheckEqual("-12.3", ShortOf(NumberFormat.Invariant, -12.3456, 3)); } public void BelowOne_IsWrittenOutWithItsSignificantDigits() { CheckEqual("0.250", ShortOf(NumberFormat.Invariant, 0.25, 3)); CheckEqual("0.0123", ShortOf(NumberFormat.Invariant, 0.0123, 3)); CheckEqual("0.000123", ShortOf(NumberFormat.Invariant, 0.00012345, 3)); CheckEqual("0.0005", ShortOf(NumberFormat.Invariant, 0.00049, 1)); CheckEqual("-0.250", ShortOf(NumberFormat.Invariant, -0.25, 3)); } public void TheSmallestWrittenOutValue_IsOneTenThousandth() { CheckEqual("0.000100", ShortOf(NumberFormat.Invariant, 0.0001, 3)); CheckEqual("0.0001", ShortOf(NumberFormat.Invariant, 0.0001, 1)); } public void BelowOneTenThousandth_IsScientific() { CheckEqual("1.00e-5", ShortOf(NumberFormat.Invariant, 0.00001, 3)); CheckEqual("-1.5e-7", ShortOf(NumberFormat.Invariant, -1.5e-7, 2)); CheckEqual("4.94e-324", ShortOf(NumberFormat.Invariant, double.Epsilon, 3)); } public void RoundingUp_CarriesAcrossTheEdgeOfTheWrittenOutRange() { CheckEqual("0.000100", ShortOf(NumberFormat.Invariant, 0.00009999, 3)); CheckEqual("1.00", ShortOf(NumberFormat.Invariant, 0.99996, 3)); } public void TheFormatsSeparatorsAndSymbols_AreUsed() { var format = new NumberFormat(".", 3, 3, "~", ",", "NaN", "Infinity", "E", ShortNotation.English); CheckEqual("~1,23K", ShortOf(format, -1234, 3)); CheckEqual("0,250", ShortOf(format, 0.25, 3)); CheckEqual("1,00E~5", ShortOf(format, 0.00001, 3)); } public void ACustomNotationsStepAndSuffixes_AreUsed() { var notation = new ShortNotation(new[] { "w", "y", "z" }, "", 4); var format = new NumberFormat(",", 3, 3, "-", ".", "NaN", "Infinity", "e", notation); CheckEqual("1234", ShortOf(format, 1234, 3)); CheckEqual("1.23w", ShortOf(format, 12345, 3)); CheckEqual("12.3w", ShortOf(format, 123456, 3)); CheckEqual("123w", ShortOf(format, 1234567, 3)); CheckEqual("1235w", ShortOf(format, 12345678, 3)); CheckEqual("1.23y", ShortOf(format, 123456789, 3)); CheckEqual("1.00z", ShortOf(format, 1e12, 3)); } public void ASuffixlessStep_NeverCutsTheWholePartShort() { var notation = new ShortNotation(new[] { "w" }, "", 4); var format = new NumberFormat(",", 3, 3, "-", ".", "NaN", "Infinity", "e", notation); CheckEqual("1235", ShortOf(format, 1234.5, 2)); } public void PastTheLastSuffix_IsScientific() { var notation = new ShortNotation(new[] { "w", "y", "z" }, "", 4); var format = new NumberFormat(",", 3, 3, "-", ".", "NaN", "Infinity", "e", notation); CheckEqual("1.00e16", ShortOf(format, 1e16, 3)); CheckEqual("1.23e20", ShortOf(format, 1.2345e20, 3)); CheckEqual("-1.00e16", ShortOf(format, -1e16, 3)); } public void RoundingUp_CarriesPastTheLastSuffixIntoScientific() { var notation = new ShortNotation(new[] { "w", "y", "z" }, "", 4); var format = new NumberFormat(",", 3, 3, "-", ".", "NaN", "Infinity", "e", notation); CheckEqual("9996z", ShortOf(format, 9.996e15, 3)); CheckEqual("1.00e16", ShortOf(format, 9.9996e15, 3)); } public void FewLetters_MakeLongSuffixes() { var notation = new ShortNotation(new[] { "K" }, "ab", 3); var format = new NumberFormat(",", 3, 3, "-", ".", "NaN", "Infinity", "e", notation); CheckEqual("1.00ba", ShortOf(format, 1e12, 3)); CheckEqual("1.00bb", ShortOf(format, 1e15, 3)); CheckEqual("1.00aaa", ShortOf(format, 1e18, 3)); } public void NaN_IsTheFormatsSymbol() { CheckEqual("NaN", ShortOf(NumberFormat.Invariant, double.NaN, 3)); } public void Infinities_AreTheFormatsSymbol() { CheckEqual("Infinity", ShortOf(NumberFormat.Invariant, double.PositiveInfinity, 3)); CheckEqual("-Infinity", ShortOf(NumberFormat.Invariant, double.NegativeInfinity, 3)); } public void FifteenDigits_AreAllWritten() { CheckEqual("1.23456789012345K", ShortOf(NumberFormat.Invariant, 1234.56789012345, 15)); CheckEqual("123.456789012345K", ShortOf(NumberFormat.Invariant, 123456.789012345, 15)); } public void TheLongestOutput_FitsMaxShortLength() { var format = new NumberFormat(",", 3, 3, "MINUS", "", "NaN", "Infinity", "EXP", ShortNotation.English); var buffer = new char[format.MaxShortLength]; int end = format.WriteShortExponent(-1.23456789012345e308, 15, buffer, 0); CheckEqual("MINUS123456789012345dt", new string(buffer, 0, end)); CheckTrue(end <= format.MaxShortLength); } public void TheLongestWrittenOutValue_FitsMaxShortLength() { var format = new NumberFormat(",", 3, 3, "MINUS", "", "NaN", "Infinity", "EXP", ShortNotation.English); var buffer = new char[format.MaxShortLength]; int end = format.WriteShortExponent(-0.000123456789012345, 15, buffer, 0); CheckEqual("MINUS0000123456789012345", new string(buffer, 0, end)); CheckTrue(end <= format.MaxShortLength); } public void ZeroDigits_Throw() { var buffer = new char[100]; CheckThrow(typeof(ArgumentOutOfRangeException)); NumberFormat.Invariant.WriteShortExponent(1, 0, buffer, 0); } public void SixteenDigits_Throw() { var buffer = new char[100]; CheckThrow(typeof(ArgumentOutOfRangeException)); NumberFormat.Invariant.WriteShortExponent(1, 16, buffer, 0); } public void WritesAtThePositionAndReturnsTheEnd() { var buffer = new char[10]; for (int i = 0; i < buffer.Length; ++i) { buffer[i] = '#'; } int end = NumberFormat.Invariant.WriteShortExponent(1250, 3, buffer, 2); CheckEqual(7, end); CheckEqual("##1.25K###", new string(buffer)); } } public class WriteNotANumberTests : TestFixture { public void WritesTheSymbolAtThePosition() { var buffer = new char[6]; int end = NumberFormat.Invariant.WriteNotANumber(buffer, 2); CheckEqual(5, end); CheckEqual("NaN", new string(buffer, 2, 3)); } } public class WriteInfinityTests : TestFixture { public void Positive_WritesTheSymbol() { var buffer = new char[10]; int end = NumberFormat.Invariant.WriteInfinity(negative: false, buffer, 0); CheckEqual("Infinity", new string(buffer, 0, end)); } public void Negative_WritesTheMinusSignFirst() { var buffer = new char[10]; int end = NumberFormat.Invariant.WriteInfinity(negative: true, buffer, 0); CheckEqual("-Infinity", new string(buffer, 0, end)); } } } }