Added double precision overloads for the garbage-free string builder appending methods; the string builder helper methods for floating point values now tell whether they successfully appended a value instead of using an assertion and appending a wrong(!) value; restored full test coverage for the whole assembly
git-svn-id: file:///srv/devel/repo-conversion/nusu@189 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
		
							parent
							
								
									237fb57fc8
								
							
						
					
					
						commit
						03eb31403d
					
				
					 2 changed files with 201 additions and 22 deletions
				
			
		|  | @ -89,6 +89,39 @@ namespace Nuclex.Support { | |||
|       Assert.AreEqual("-12345", builder.ToString()); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies that a positive long integer is correctly appended to a string builder | ||||
|     /// </summary> | ||||
|     [Test] | ||||
|     public void TestAppendPositiveLong() { | ||||
|       StringBuilder builder = new StringBuilder(); | ||||
|       StringBuilderHelper.Append(builder, 12345L); | ||||
| 
 | ||||
|       Assert.AreEqual("12345", builder.ToString()); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies that a long integer with value 0 is correctly appended to a string builder | ||||
|     /// </summary> | ||||
|     [Test] | ||||
|     public void TestAppendNullLong() { | ||||
|       StringBuilder builder = new StringBuilder(); | ||||
|       StringBuilderHelper.Append(builder, 0L); | ||||
| 
 | ||||
|       Assert.AreEqual("0", builder.ToString()); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies that a negative long integer is correctly appended to a string builder | ||||
|     /// </summary> | ||||
|     [Test] | ||||
|     public void TestAppendNegativeLong() { | ||||
|       StringBuilder builder = new StringBuilder(); | ||||
|       StringBuilderHelper.Append(builder, -12345L); | ||||
| 
 | ||||
|       Assert.AreEqual("-12345", builder.ToString()); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies that negative floating point values are correctly converted | ||||
|     /// </summary> | ||||
|  | @ -133,6 +166,81 @@ namespace Nuclex.Support { | |||
|       Assert.AreEqual("1000000000.0", builder.ToString()); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies the behavior of the helper with unsupported floating point values | ||||
|     /// </summary> | ||||
|     [Test] | ||||
|     public void TestAppendOutOfRangeFloat() { | ||||
|       StringBuilder builder = new StringBuilder(); | ||||
|       Assert.IsFalse(StringBuilderHelper.Append(builder, float.PositiveInfinity)); | ||||
|       Assert.IsFalse(StringBuilderHelper.Append(builder, float.NegativeInfinity)); | ||||
|       Assert.IsFalse(StringBuilderHelper.Append(builder, float.NaN)); | ||||
|       Assert.IsFalse(StringBuilderHelper.Append(builder, 0.000000059604644775390625f)); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies that negative double precision floating point values are | ||||
|     ///   correctly converted | ||||
|     /// </summary> | ||||
|     [Test] | ||||
|     public void TestAppendNegativeDouble() { | ||||
|       StringBuilder builder = new StringBuilder(); | ||||
|       StringBuilderHelper.Append(builder, -32.015625); | ||||
| 
 | ||||
|       Assert.AreEqual("-32.015625", builder.ToString()); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies that positive double precision floating point values are | ||||
|     ///   correctly converted | ||||
|     /// </summary> | ||||
|     [Test] | ||||
|     public void TestAppendPositiveDouble() { | ||||
|       StringBuilder builder = new StringBuilder(); | ||||
|       StringBuilderHelper.Append(builder, 10.0625); | ||||
| 
 | ||||
|       Assert.AreEqual("10.0625", builder.ToString()); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies that very small double precision floating point values are | ||||
|     ///   correctly converted | ||||
|     /// </summary> | ||||
|     [Test] | ||||
|     public void TestAppendSmallDouble() { | ||||
|       StringBuilder builder = new StringBuilder(); | ||||
|       StringBuilderHelper.Append(builder, 0.00390625); | ||||
| 
 | ||||
|       Assert.AreEqual("0.00390625", builder.ToString()); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies that very large double precision floating point values are | ||||
|     ///   correctly converted | ||||
|     /// </summary> | ||||
|     [Test] | ||||
|     public void TestAppendHugeDouble() { | ||||
|       StringBuilder builder = new StringBuilder(); | ||||
|       StringBuilderHelper.Append(builder, 1000000000000000000.0); | ||||
| 
 | ||||
|       Assert.AreEqual("1000000000000000000.0", builder.ToString()); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies the behavior of the helper with unsupported double precision | ||||
|     ///   floating point values | ||||
|     /// </summary> | ||||
|     [Test] | ||||
|     public void TestAppendOutOfRangeDouble() { | ||||
|       StringBuilder builder = new StringBuilder(); | ||||
|       Assert.IsFalse(StringBuilderHelper.Append(builder, double.PositiveInfinity)); | ||||
|       Assert.IsFalse(StringBuilderHelper.Append(builder, double.NegativeInfinity)); | ||||
|       Assert.IsFalse(StringBuilderHelper.Append(builder, double.NaN)); | ||||
|       Assert.IsFalse( | ||||
|         StringBuilderHelper.Append(builder, 1.1102230246251565404236316680908e-16) | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Verifies that the contents of a string builder can be cleared | ||||
|     /// </summary> | ||||
|  |  | |||
|  | @ -96,19 +96,24 @@ namespace Nuclex.Support { | |||
|     /// </summary> | ||||
|     /// <param name="builder">String builder the value will be appended to</param> | ||||
|     /// <param name="value">Value that will be appended to the string builder</param> | ||||
|     public static void Append(StringBuilder builder, float value) { | ||||
|     /// <returns>Whether the value was inside the algorithm's supported range</returns> | ||||
|     /// <remarks> | ||||
|     ///   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. | ||||
|     /// </remarks> | ||||
|     public static bool Append(StringBuilder builder, float value) { | ||||
|       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 | ||||
|       const int NumericBitCount = 31; // Bits without sign | ||||
| 
 | ||||
|       // You do not modify these as they're calculated based on the  | ||||
|       // You don't need modify these as they're calculated based on the  | ||||
|       const int FractionalBits = (2 << FractionalBitCount) - 1; | ||||
|       const int HighestFractionalBit = (1 << FractionalBitCount); | ||||
|       const int FractionalBitCountPlusOne = FractionalBitCount + 1; | ||||
| 
 | ||||
|       int intValue = FloatHelper.ReinterpretAsInt(value); | ||||
| 
 | ||||
|       int exponent = ((intValue >> FractionalBitCount) & ExponentBits) - ExponentBias; | ||||
|       int mantissa = (intValue & FractionalBits) | HighestFractionalBit; | ||||
| 
 | ||||
|  | @ -116,7 +121,9 @@ namespace Nuclex.Support { | |||
|       int fractional; | ||||
|       if(exponent >= 0) { | ||||
|         if(exponent >= FractionalBitCount) { | ||||
|           Debug.Assert(exponent < NumericBitCount); | ||||
|           if(exponent >= NumericBitCount) { | ||||
|             return false; | ||||
|           } | ||||
|           integral = mantissa << (exponent - FractionalBitCount); | ||||
|           fractional = 0; | ||||
|         } else { | ||||
|  | @ -124,26 +131,13 @@ namespace Nuclex.Support { | |||
|           fractional = (mantissa << (exponent + 1)) & FractionalBits; | ||||
|         } | ||||
|       } else { | ||||
|         Debug.Assert(exponent >= -FractionalBitCount); | ||||
|         if(exponent < -FractionalBitCount) { | ||||
|           return false; | ||||
|         } | ||||
|         integral = 0; | ||||
|         fractional = (mantissa & FractionalBits) >> -(exponent + 1); | ||||
|       } | ||||
| /*       | ||||
|       if(exponent >= NumericBitCount) { | ||||
|         throw new ArgumentException("Value too large", "value"); | ||||
|       } else if(exponent < -FractionalBitCount) { | ||||
|         throw new ArgumentException("Value too small", "value"); | ||||
|       } else if(exponent >= FractionalBitCount) { | ||||
|         integral = mantissa << (exponent - FractionalBitCount); | ||||
|         fractional = 0; | ||||
|       } else if(exponent >= 0) { | ||||
|         integral = mantissa >> (FractionalBitCount - exponent); | ||||
|         fractional = (mantissa << (exponent + 1)) & FractionalBits; | ||||
|       } else { // exp2 < 0 | ||||
|         integral = 0; | ||||
|         fractional = (mantissa & FractionalBits) >> -(exponent + 1); | ||||
|       } | ||||
| */ | ||||
| 
 | ||||
|       // Build the integral part       | ||||
|       if(intValue < 0) { | ||||
|         builder.Append('-'); | ||||
|  | @ -167,6 +161,83 @@ namespace Nuclex.Support { | |||
|           fractional &= FractionalBits; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     ///   Appends a double precision floating point value to a string builder | ||||
|     ///   without generating garbage | ||||
|     /// </summary> | ||||
|     /// <param name="builder">String builder the value will be appended to</param> | ||||
|     /// <param name="value">Value that will be appended to the string builder</param> | ||||
|     /// <returns>Whether the value was inside the algorithm's supported range</returns> | ||||
|     /// <remarks> | ||||
|     ///   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. | ||||
|     /// </remarks> | ||||
|     public static bool Append(StringBuilder builder, double value) { | ||||
|       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 | ||||
|       const int NumericBitCount = 63; // Bits without sign | ||||
| 
 | ||||
|       // You don't need modify these as they're calculated based on the  | ||||
|       const long FractionalBits = (2L << FractionalBitCount) - 1; | ||||
|       const long HighestFractionalBit = (1L << FractionalBitCount); | ||||
|       const int FractionalBitCountPlusOne = FractionalBitCount + 1; | ||||
| 
 | ||||
|       long intValue = FloatHelper.ReinterpretAsLong(value); | ||||
|       long exponent = ((intValue >> FractionalBitCount) & ExponentBits) - ExponentBias; | ||||
|       long mantissa = (intValue & FractionalBits) | HighestFractionalBit; | ||||
| 
 | ||||
|       long integral; | ||||
|       long fractional; | ||||
|       if(exponent >= 0) { | ||||
|         if(exponent >= FractionalBitCount) { | ||||
|           if(exponent >= NumericBitCount) { | ||||
|             return false; | ||||
|           } | ||||
|           integral = mantissa << (int)(exponent - FractionalBitCount); | ||||
|           fractional = 0; | ||||
|         } else { | ||||
|           integral = mantissa >> (int)(FractionalBitCount - exponent); | ||||
|           fractional = (mantissa << (int)(exponent + 1)) & FractionalBits; | ||||
|         } | ||||
|       } else { | ||||
|         if(exponent < -FractionalBitCount) { | ||||
|           return false; | ||||
|         } | ||||
|         integral = 0; | ||||
|         fractional = (mantissa & FractionalBits) >> -(int)(exponent + 1); | ||||
|       } | ||||
| 
 | ||||
|       // Build the integral part       | ||||
|       if(intValue < 0) { | ||||
|         builder.Append('-'); | ||||
|       } | ||||
|       if(integral == 0) { | ||||
|         builder.Append('0'); | ||||
|       } else { | ||||
|         recursiveAppend(builder, integral); | ||||
|       } | ||||
| 
 | ||||
|       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; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary>Recursively appends a number's characters to a string builder</summary> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue