views:

832

answers:

5

I have the need to format a decimal number as currency but I do not wish for any rounding to occur in the process.

For example (example culture is en-US)

Dim money = 1234.556789D
money.ToString("C") ' Yields $1,234.56 (notice the rounding & truncating)

money.ToString("C99") ' Yields $1,234.556789000000000000....0 -- close but not perfect, those trailing zeros are unwanted.

' I want something that would result in this...
money.ToString("??") ' Yields $1,234.56789

' likewise....
money = 0.1D
money.ToString("??") ' Yields $0.10  (notice that it at least matches the culture's currency format -- two decimal places)

Assuming all of the users of this application would be using en_US rules I could make that "??" be something hard-coded like "$#,##0.00################################################################" -- but that makes my stomach churn. Is there a built-in way to accomplish what I'm after?

A: 

Yes. Here's the answer from another forum:

http://forums.asp.net/p/1266337/2380486.aspx

Nissan Fan
If you mean doing something like...Dim money = 1234.556789DDim c = New Globalization.CultureInfo(System.Globalization.CultureInfo.CurrentCulture.Name)c.NumberFormat.CurrencyDecimalDigits = 99x.ToString("C", c)Then I get trailing zeros for all non-filled decimal digits. Is this what you were suggesting -- or did I misread the link you sent me?
ckittel
+1  A: 

ToString("C20") -- C takes a precision suffix

EDIT: oops, didn't read the question apparently.

.ToString("C20").Trim('0') seems like the fix, but this doesn't work when you're using string.Format...

Jimmy
I think he only wants to show the relevant digits however. This *always* shows 20 digits.
Thorarin
Correct, this would not work for me. for the reason Thorarin said.
ckittel
The .Trim('0') would turn 0.1D into $0.1 instead of $0.10 ... I had thought of this as well. money.ToString("C99").TrimEnd("0"c) .. then I thought about chaining it with PadRight(n,"0").
ckittel
+1  A: 

You could do something like this...

var money = 1234.556789D;
Console.WriteLine(money.ToString(GetFormat(money)));

money = .1D;

Console.WriteLine(money.ToString(GetFormat(money)));

using the following method to get the format string...

static string GetFormat(double input)
{
    // get the number of decimal places to show
    int length = input.ToString().Length - input.ToString().IndexOf(".") - 1;

    // return the currency format string to use with decimal.ToString()
    return string.Format("C{0}", length < 2 ? 2 : length);
}

If you wanted to take it a step further, you could also wrap all of this into an extension method so that you didn't have to call the GetFormat() method from inside ToString() - that might make things look a bit cleaner.

Scott Ivey
Yeah, I probably should have made it clearer that I could roll-my-own, like your good example/suggestion here of doing so; if I went under the assumption that it was always going to be in en-US format. I was hoping for something more "baked in" so to speak to handle other cultures as well.This method or one similar would also work with other cultures if I replaced all of the hard-coded values with the current culture-specific ones.
ckittel
A: 

You could try writing a regular expression to get rid of all of those trailing 0's.

Aaron Smith
A: 

Seeing that there doesn't seem to be a built-in way of doing this, I ended up rolling my own extension method that looks like...

Public Function ToUnroundedCurrency(ByVal value As Decimal) As String
    Dim valueAsString As String = value.ToString() ' Some loss of precision happens here, but it is not a concern.

    Dim decimalLocation As Integer = valueAsString.IndexOf(".")

    ' Get number of digits after the decimal place in the number
    Dim numberOfDecimalPlaces As Integer = 0
    If (decimalLocation > 0) Then numberOfDecimalPlaces = valueAsString.Length - decimalLocation - 1

    ' The currency formatter has a limit of 99 decimal places -- due to the decimal -> ToString() conversion above, this will never happen, but being defensive anyway.
    If (numberOfDecimalPlaces > 99) Then
        numberOfDecimalPlaces = 99
    ElseIf (numberOfDecimalPlaces < CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalDigits) Then
        ' also make sure we at least have the minimum required decimal places
        numberOfDecimalPlaces = CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalDigits
    End If

    Return value.ToString("C" & numberOfDecimalPlaces)
End Function

I noted some negligible loss of precision. We (or anybody probably) wouldn't be dealing with decimal values fractional enough enough to ever run into the limitations.

ckittel