简体   繁体   中英

How can I have Decimal.TryParse parse 0.0?

Is there a way to get Decimal.TryParse to parse a string value of "0.0" or "00.00" or "000.000" as 0?

I have tried setting NumberStyles to Any.

Using the InvariantCulture, decimal.TryParse does successfully interpret all of those strings as zero. This code, for example:

decimal num = 66;
foreach (var str in new string[] { "0.0", "00.00", "000.000" })
{
    if (!decimal.TryParse(str, out num))
    {
        Console.WriteLine( "fail" );
    }
    Console.WriteLine(num);
}

Produces this output:

0.0
0.00
0.000

Perhaps, the issue is printing the values, not parsing the values? If that's the case, simply use a format string that specifies that the decimal places are optional.

Console.WriteLine( num.ToString( "#0.#" ) );

Produces

0
0
0

Is there a way to get Decimal.TryParse to parse a string value of "0.0" or "00.00" or "000.000" as 0?

I am interpreting your question to mean. Say I take the strings "0.0" , "00.00" and "000.000" ask Decimal.TryParse to parse them. When I write out the resulting decimal to the console I see 0.0 , 0.00 and 0.000 respectively. Is there a way to get Decimal.TryParse to return a decimal in all these cases that will be written to the console as 0 ?

No. Think about why this should be. Decimal types represent precise numbers; in certain circles, 0.00 would be considered more precise than 0.0 which would be considered more precise than 0 . If Decimal.TryParse truncated that precision than the Decimal type would not be useful for these purposes.

That said, it's easy enough to just trim the trailing zeros before calling parse:

static char[] whitespaceAndZero = new[] {
    ' ',
    '\t',
    '\r',
    '\n',
    '\u000b', // vertical tab
    '\u000c', // form feed
    '0'
};
static string TrimEndWhitespaceAndZeros(string s) {
    return s.Contains('.') ? s.TrimEnd(whitespaceAndZero) : s;
}

static bool TryParseAfterTrim(string s, out decimal d) {
    return Decimal.TryParse(TrimEndWhiteSpaceAndZeros(s), out d);
}

Usage:

string s = "0.00";
decimal d;
TryParseAfterTrim(s, out d);
Console.WriteLine(d);

Output:

0

Please note that the above only shows the crux of how to solve your problem. It is up to you to decide whether or not and how you are going to handle localization issues. At a minimum, before putting this into production you should consider replacing the hard-coded '.' with CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator . You should consider having an overload of TryParseAfterTrim with the same parameter list as Decimal.TryParse . That is:

bool TryParseAfterTrim(
    string s,
    NumberStyle style,
    IFormatProvider provider,
    out decimal result
)

Preserving trailing zeroes in decimal values was introduced in .NET 1.1 for more strict conformance with the ECMA CLI specification. See this answer to a similar question .

While the original question is limited to removing trailing zeroes when parsing a string, I think it's important to understand why and under what circumstances trailing zeroes are preserved in a decimal value's internal representation.

Trailing zeroes can appear in a decimal value as a result of :

a) parsing input that has trailing zeroes, as in the original post, or

b) as a result of a calculation. For example: multiplying decimal values 1.2 * 1.5 gives a result of 1.80: this is because multiplying two values that are each accurate to one decimal place gives a result that is accurate to two decimal places - and the second decimal place is therefore preserved even if its value is zero.

What to do about trailing zeroes in the internal decimal representation? In general do nothing: you can format on output to your desired number of decimals, so they won't hurt.

Decimal is mainly used for financial calculations, and it's generally desirable to preserve trailing zeroes so that an amount rounded to the nearest cent is represented as 1.20 rather than 1.2. Normally when using decimals, you will be working to a fixed precision (perhaps to the nearest cent in a retail application, or to the nearest hundredth of a cent when calculating mobile phone usage charges). So your application logic will explicitly take care of rounding to a fixed number of decimals using explicit rounding rules, eg a tax calculation that rounds to the nearest cent, using the MidpointRounding.AwayFromZero:

decimal price = 1.20M;   
decimal taxRate = 0.175; // 17.5%
decimal taxAmount = Math.Round(price*taxRate, 2, 
                      MidpointRounding.AwayFromZero);

If you're not working to a fixed number of decimals in this way, you might consider whether you should be using double rather than decimal.

Nevertheless, there may occasionally be a requirement to remove trailing zeroes, while retaining all significant digits. AFAIK, there isn't a built-in method for this.

If you need to do so, a basic solution would be to format as a string using the standard format string "G28" (equivalent to the custom format string "0.############################"), then parse the result back to a decimal.

An alternative way of removing trailing zeroes without converting to/from a string (probably faster, though I haven't measured it) is to use Math.Round - eg the following method:

    static decimal RemoveTrailingZeroes(decimal value)
    {
        const int MaxDecimals = 28;
        decimal roundedValue;
        for (int decimals = 0; decimals < MaxDecimals; decimals++)
        {
            roundedValue = Math.Round(value, decimals);
            if (value == roundedValue) return roundedValue;
        }
        return value;
    }

What is it that is not working? This works fine for me (tested with "0.0", "00.00" and "000.000"):

decimal d;
if (decimal.TryParse("0.0", NumberStyles.Any, CultureInfo.InvariantCulture, out d))
{
    // use d
}

I humbly submit this solution:

    decimal processedValue = value == 0 ? 0 : value;

Does this not do the job?

Here is a complete example:

string valueString = "0.000";
decimal value;
bool isValid = decimal.TryParse(valueString, out value);
if (isValid)
{
    decimal processedValue = value == 0 ? 0 : value;
    System.Diagnostics.Debug.Print("value: {0}, processedValue: {1}", value, processedValue);
}

// This outputs 0 given 0.0 as a string though....

 decimal d;

 decimal.TryParse("0.0", out d);

 string s = String.Format("{0:0}", d);

Why don't you use int.TryParse? or double.TryParse?

在Console.Writeline中,应用字符串格式以保持双零精度。

I see what you mean. The following code:

decimal number = -1.0M;
bool res1 = decimal.TryParse("0.0", out number);
Console.WriteLine(string.Format("number = {0}", number));
number = -1.0M;
bool res2 = decimal.TryParse("00.00", out number);
Console.WriteLine(string.Format("number = {0}", number));
number = -1.0M;
bool res3 = decimal.TryParse("000.000", out number);
Console.WriteLine(string.Format("number = {0}", number));
number = -1.0M;
bool res4 = decimal.TryParse("0000.0000", out number);
Console.WriteLine(string.Format("number = {0}", number));

prints:

number = 0.0
number = 0.00
number = 0.000
number = 0.0000

So the input does affect the output. But checking the value of number using the debugger shows "0" in the tooltip and Locals window which indicates that it's a formatting issue rather than a fundamental problem with the value being stored.

However, as others have said trim the trailing zeros (and decimal point) before converting.

One way to get rid of trailing zeros after decimal point would be eliminating them in the decimal.

The brute force way would be meddling with the internal structure of the decimal:

public static Decimal TrimFractionZeros(this Decimal zahl)
{
  int[] bits = decimal.GetBits(zahl);
  byte decimals = (Byte)(bits[3] >> 16);
  bool negativ = (bits[3] >> 31) != 0;
  zahl = new decimal(bits[0], bits[1], bits[2], false, 0);
  while ((decimals > 0) && ((zahl % 10) == 0))
  {
    zahl /= 10;
    decimals--;
  }
  bits = decimal.GetBits(zahl);
  return new decimal(bits[0], bits[1], bits[2], negativ, decimals);
}

Not pretty, not fast, but it will change the internal representation of "0.000" to "0" in a decimal.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM