diff --git a/Source/StringBuilderHelper.Test.cs b/Source/StringBuilderHelper.Test.cs index 3f63c91..f43766e 100644 --- a/Source/StringBuilderHelper.Test.cs +++ b/Source/StringBuilderHelper.Test.cs @@ -166,6 +166,26 @@ namespace Nuclex.Support { Assert.AreEqual("1000000000.0", builder.ToString()); } + /// Tests whether the number of decimal places can be restricted + [Test] + public void TestAppendFloatLimitDecimalPlaces() { + StringBuilder builder = new StringBuilder(); + StringBuilderHelper.Append(builder, 0.00390625f, 3); + + Assert.AreEqual("0.003", builder.ToString()); + } + + /// + /// Verifies that a float with no decimal places is correctly appended + /// + [Test] + public void TestAppendFloatWithoutDecimalPlaces() { + StringBuilder builder = new StringBuilder(); + StringBuilderHelper.Append(builder, 0.00390625f, 0); + + Assert.AreEqual("0", builder.ToString()); // Note: no rounding! + } + /// /// Verifies the behavior of the helper with unsupported floating point values /// @@ -226,6 +246,26 @@ namespace Nuclex.Support { Assert.AreEqual("1000000000000000000.0", builder.ToString()); } + /// Tests whether the number of decimal places can be restricted + [Test] + public void TestAppendDoubleLimitDecimalPlaces() { + StringBuilder builder = new StringBuilder(); + StringBuilderHelper.Append(builder, 0.00390625, 3); + + Assert.AreEqual("0.003", builder.ToString()); // Note: no rounding! + } + + /// + /// Verifies that a double with no decimal places is correctly appended + /// + [Test] + public void TestAppendDoubleWithoutDecimalPlaces() { + StringBuilder builder = new StringBuilder(); + StringBuilderHelper.Append(builder, 0.00390625, 0); + + Assert.AreEqual("0", builder.ToString()); + } + /// /// Verifies the behavior of the helper with unsupported double precision /// floating point values diff --git a/Source/StringBuilderHelper.cs b/Source/StringBuilderHelper.cs index 590e483..e3242d7 100644 --- a/Source/StringBuilderHelper.cs +++ b/Source/StringBuilderHelper.cs @@ -103,6 +103,22 @@ namespace Nuclex.Support { /// is returned and the traditional double.ToString() method can be used. /// public static bool Append(StringBuilder builder, float value) { + return Append(builder, value, int.MaxValue); + } + + /// + /// Appends a floating point value to a string builder without generating garbage + /// + /// String builder the value will be appended to + /// Value that will be appended to the string builder + /// Maximum number of decimal places to display + /// Whether the value was inside the algorithm's supported range + /// + /// Uses an algorithm that covers the sane range of possible values but will + /// fail to render extreme values, NaNs and infinity. In these cases, false + /// is returned and the traditional double.ToString() method can be used. + /// + public static bool Append(StringBuilder builder, float value, int decimalPlaces) { const int ExponentBits = 0xFF; // Bit mask for the exponent bits const int FractionalBitCount = 23; // Number of bits for fractional part const int ExponentBias = 127; // Bias subtraced from exponent @@ -148,17 +164,24 @@ namespace Nuclex.Support { recursiveAppend(builder, integral); } - builder.Append('.'); + if(decimalPlaces > 0) { + builder.Append('.'); - // Build the fractional part - if(fractional == 0) { - builder.Append('0'); - } else { - while(fractional != 0) { - fractional *= 10; - int digit = (fractional >> FractionalBitCountPlusOne); - builder.Append(numbers[digit]); - fractional &= FractionalBits; + // Build the fractional part + if(fractional == 0) { + builder.Append('0'); + } else { + while(fractional != 0) { + fractional *= 10; + int digit = (fractional >> FractionalBitCountPlusOne); + builder.Append(numbers[digit]); + fractional &= FractionalBits; + + --decimalPlaces; + if(decimalPlaces == 0) { + break; + } + } } } @@ -178,6 +201,23 @@ namespace Nuclex.Support { /// is returned and the traditional double.ToString() method can be used. /// public static bool Append(StringBuilder builder, double value) { + return Append(builder, value, int.MaxValue); + } + + /// + /// Appends a double precision floating point value to a string builder + /// without generating garbage + /// + /// String builder the value will be appended to + /// Value that will be appended to the string builder + /// Maximum number of decimal places to display + /// Whether the value was inside the algorithm's supported range + /// + /// Uses an algorithm that covers the sane range of possible values but will + /// fail to render extreme values, NaNs and infinity. In these cases, false + /// is returned and the traditional double.ToString() method can be used. + /// + public static bool Append(StringBuilder builder, double value, int decimalPlaces) { const long ExponentBits = 0x7FF; // Bit mask for the exponent bits const int FractionalBitCount = 52; // Number of bits for fractional part const int ExponentBias = 1023; // Bias subtraced from exponent @@ -223,17 +263,24 @@ namespace Nuclex.Support { recursiveAppend(builder, integral); } - builder.Append('.'); + if(decimalPlaces > 0) { + builder.Append('.'); - // Build the fractional part - if(fractional == 0) { - builder.Append('0'); - } else { - while(fractional != 0) { - fractional *= 10; - long digit = (fractional >> FractionalBitCountPlusOne); - builder.Append(numbers[digit]); - fractional &= FractionalBits; + // Build the fractional part + if(fractional == 0) { + builder.Append('0'); + } else { + while(fractional != 0) { + fractional *= 10; + long digit = (fractional >> FractionalBitCountPlusOne); + builder.Append(numbers[digit]); + fractional &= FractionalBits; + + --decimalPlaces; + if(decimalPlaces == 0) { + break; + } + } } }