Monday, September 26, 2005

C# formatting double to specific number of digits

Wow! I'm shocked I even had to work on this one. I had a simple problem of having to take a value from a textbox and make sure that it was formatted with only 2 decimals. The textbox doesn't prevent users from typing more than 2 digits to right of the decimal, but it needs to strip any more than 2 off because the users know that this field is used for currency and any additional is assumed to be a mistake. For example, if the number 1234.556 is typed I need to format it to be 1,234.55

I thought that this would be a no-brainer, I'll just format Number with 2 decimal places. BUZZZ! Wrong answer!. First I tried the expected formats.

double test = 1234.556;
Console.WriteLine(test.ToString("N2")); // 1,234.56
Console.WriteLine(test.ToString("F2")); // 1234.56
Console.WriteLine(test.ToString("C2")); // $1,234.56

All 3 of these rounded up rather than dropping the extra I figured I'd use my old VB6 trick and just convert it into an integer then back, so I tried:
double test = 1234.556;
int testval = Convert.ToInt32(test * 100);
Console.WriteLine(testval.ToString()); //123456

This tells me that behind the scenes this number is being rounded up before it's being converted to an integer. I just want the actual value of whole number portion here. Grr!

After lots of searching online and wrenching my brain for nearly 2 days, I decide to go back to my roots. In C if I had to do this I'd just make it a string, then find the decimal and replace it with a null to cut the string there, then I would convert the rest back. I think I can imitate that in the more advanced C#. So I wrote my own function.

private double TrimDoubleToXDigits(double initialValue, int numDigits)
{
if( numDigits < 0 )
{
throw new Exception("should not be negative");
}

double divisor = Math.Pow(10, numDigits);
// multiply by 100 to get all keeper values left of the decimal.
double result = initialValue * divisor;
string testValNoDec = result.ToString();
// truncate everything to the right of the decimal.
if( testValNoDec.IndexOf(".") != -1 )
{
// remove the decimal and everything else.
testValNoDec = testValNoDec.Substring(0, testValNoDec.IndexOf("."));
}

result = Convert.ToDouble(testValNoDec) / divisor;
return result;
}


It's easy to use, you just call it as shown here.
double trimmedValue = TrimDoubleToXDigits( 1234.556, 2 );
Console.WriteLine("Resulting value : " + trimmedValue.ToString("N2") );
// 1,234.55

trimmedValue = TrimDoubleToXDigits( 1234.556789, 3 );
Console.WriteLine("Resulting value : " + trimmedValue.ToString("N3") );
// 1,234.556

I know I could have had a more robust function with better error checking, but I have enough control over the environment and use of this funciton that it serves it's purpose. Any more time at this point would only be putting me further behind schedule.