Question: How do I strip trailing zeros after the decimal point from a string containing a number?
Mind-Boggling Answer: This C# solution got 24 up-votes on StackOverflow, and the author has almost half-a-million reputation points for his 21,000 answers, so it must be right!
using System; using System.Numerics; public static class DecimalExtensions { // Avoiding implicit conversions just for clarity private static readonly BigInteger Ten = new BigInteger(10); private static readonly BigInteger UInt32Mask = new BigInteger(0xffffffffU); public static decimal Normalize(this decimal input) { unchecked { int[] bits = decimal.GetBits(input); BigInteger mantissa = new BigInteger((uint) bits[0]) + (new BigInteger((uint) bits[1]) << 32) + (new BigInteger((uint) bits[2]) << 64); int sign = bits[3] & int.MinValue; int exponent = (bits[3] & 0xff0000) >> 16; // The loop condition here is ugly, because we want // to do both the DivRem part and the exponent check :( while (exponent > 0) { BigInteger remainder; BigInteger divided = BigInteger.DivRem(mantissa, Ten, out remainder); if (remainder != BigInteger.Zero) { break; } exponent--; mantissa = divided; } // Okay, now put it all back together again... bits[3] = (exponent << 16) | sign; // For each 32 bits, convert the bottom 32 bits into a uint (which won't // overflow) and then cast to int (which will respect the bits, which // is what we want) bits[0] = (int) (uint) (mantissa & UInt32Mask); mantissa >>= 32; bits[1] = (int) (uint) (mantissa & UInt32Mask); mantissa >>= 32; bits[2] = (int) (uint) (mantissa & UInt32Mask); return new decimal(bits); } } class Program { static void Main(string[] args) { Check(6.000m); Check(6000m); Check(6m); Check(60.00m); Check(12345.00100m); Check(-100.00m); } static void Check(decimal d) { Console.WriteLine("Before: {0} - after: {1}", d, d.Normalize()); } } }
Gasp! [shudder]
...words cannot express the horror.How about this instead?
Just... write some code to look for trailing zeros after the decimal point and remove them from the string.Sound familiar? It's the original Question almost word for word.
CREATE FUNCTION strip_trailing_zeros_after_decimal_point ( IN @input_number VARCHAR ( 100 ) ) RETURNS VARCHAR ( 100 ) BEGIN DECLARE @output_number VARCHAR ( 100 ); SET @output_number = TRIM ( COALESCE ( @input_number, '' ) ); IF LOCATE ( @output_number, '.' ) > 0 THEN WHILE RIGHT ( @output_number, 1 ) = '0' LOOP SET @output_number = LEFT ( @output_number, LENGTH ( @output_number ) - 1 ); END LOOP; IF RIGHT ( @output_number, 1 ) = '.' THEN SET @output_number = LEFT ( @output_number, LENGTH ( @output_number ) - 1 ); END IF; END IF; RETURN @output_number; END;
- The SET on line 7 does a bit of input editing: it turns NULL into '' and gets rid of leading and trailing spaces.
- The IF LOCATE on line 9 makes sure the function doesn't do anything more if there's no decimal point at all.
- The WHILE loop on lines 11 to 13 zaps each trailing zero, one at a time, from the RIGHT end of the string.
- The IF RIGHT ... SET on lines 15 to 17 gets rid of the decimal point if it ends up being the last thing on the line.
SELECT '000' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123.' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123.0' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123.00' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123.01' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123.10' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123.010' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123.456' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123.4560' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123.45600' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y UNION SELECT '123000' AS x, strip_trailing_zeros_after_decimal_point ( x ) AS y ORDER BY 1; x y 000 000 123 123 123. 123 123.0 123 123.00 123 123.01 123.01 123.010 123.01 123.10 123.1 123.456 123.456 123.4560 123.456 123.45600 123.456 123000 123000...well, it works for trailing zeros, the "000" is a formatting question for another day.
And I'll bet you could do better! (even simpler, even more straightforward)
No comments:
Post a Comment