Keugyeol
Keugyeol

Reputation: 2435

Converting a string with commas to double and int are different. Why?

I am migrating from an old MFC GUI to C#.

I was building a Form-based GUI when I've got an unexpected exception converting a string to an integer type. I assumed it would work the same as converting string to double.

string str = "1,000";
double dthou = Convert.ToDouble(str);   // OK
int ithou = Convert.ToInt32(str);       // raises an exception

Conversion to double gives correct value: 1000.0. For int conversion, I was able to get a solution : Convert.ToInt32() a string with Commas.

But I am curious if there were any reason behind this. Or, am I missing something?

I was able to find a similar, but not exactly a duplicate question : Number parsing weirdness

[EDIT] after learning about the culture issue.

I am in a kind of a culture-shock because until now, in Korea, both floating point number and integer numbers are expressed with "," for thousands group and "." for decimal point (at least in the real world, in Korea, I mean, I think... ). I guess I will have to accept current settings of MS Visual Studio and carry on.

[EDIT2] after sleeping over this issue.

I think it's more of the inconsistent handling of the formatted string. ToDouble accepts strings with thousands separator (in my culture, comma), but ToInt32 does not. If ToDouble is float | allowThousands, then why could'nt ToInt32 have been integer | allowThousands is what I am asking.

Upvotes: 6

Views: 5768

Answers (3)

Soner Gönül
Soner Gönül

Reputation: 98750

In your profile, it says you are from South Korea. That's why I assume your current culture is ko-KR. (And you said as well.)

And it's NumberDecimalSeparator is . but it's NumberGroupSeparator is ,

enter image description here

Your Convert.ToDouble works and it assumes your , is a thousands seperator, not decimal seperator. That's why your dthou will be 1000 not 1.

Convert.ToInt32(string) uses Int32.Parse(string, CultureInfo.CurrentCulture) explicitly and this method implemented like;

public static int Parse(String s, IFormatProvider provider)
{
    return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}

As you can see this method uses NumberStyles.Integer as a default. And that's why your string can be successfully parsed only it contasion one of these;

  • Leading white space
  • Trailing white space
  • Leading sign (positive or negative)

And since your string has thousands seperator or decimal seperator (this depends on which one you used for) this method throws exception.

Instead of that, you can use Int32.Parse(String, NumberStyles, IFormatProvider) overload which you can specify your NumberStyles like NumberStyles.AllowDecimalPoint or NumberStyles.AllowThousands

As an example;

string str = "1,000";
int ithou = Int32.Parse(str, NumberStyles.AllowThousands,
                        new CultureInfo("ko-KR"));
Console.WriteLine(ithou); // Prints 1000

If you want to get 1 as a result, you can use CultureInfo.Clone method to your culture and set it's NumberDecimalSeparator and NumberGroupSeparator properties like;

string str = "1,000";
CultureInfo c = (CultureInfo)CultureInfo.GetCultureInfo("ko-KR").Clone();
c.NumberFormat.NumberDecimalSeparator = ",";
c.NumberFormat.NumberGroupSeparator = ".";
int dthou = Int32.Parse(str, NumberStyles.AllowDecimalPoint, c);
Console.WriteLine(dthou ); // Prints 1

I don't think it's a cultural problem. It is the inconsistent handling of the formatted string. ToDouble accepts strings with comma, but ToInt32 does not. It's like going back to the original question again, but couldn't ToInt32 be implemented to accept the comma just like ToDouble function?

Oh my dear friend, you are still thinking wrong..

Everything is a culture problem in your case. There is no such a thing "Convert.ToDouble() accepts strings with comma, but Convert.ToInt32() does not".

Let's look at one more time how these methods are implemented.

Convert.ToDouble(string) uses Double.Parse(value, CultureInfo.CurrentCulture) explicitly and it is implemented like;

public static double Parse(String s, IFormatProvider provider)
{
     return Parse(s, NumberStyles.Float| NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider));
}

With this NumberStyles.Float| NumberStyles.AllowThousands, you can use both decimal point or thousands separator in your code but , is your culture's NumberGroupSeparator not NumberDecimalSeparator. That's why your string will be parsed as a thousands seperetor. There is no such a thing Convert.ToDouble uses string with comma. It can be use your current culture's NumberDecimalSeparator or NumberGroupSeparator depends on which character your string has. If both were equal, NumberDecimalSeparator will be dominant and it will be used.

Convert.ToInt32(string) uses Int32.Parse(string, CultureInfo.CurrentCulture) explicitly and it's implemented like;

public static int Parse(String s, IFormatProvider provider)
{
    return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}

As I said before, NumberStyles.Integer allows three things for your string; leading white space, trailing white space and leading positive or negative sign. It can't be parse if your string has decimal separator or thousands separator no matter it is comma or dot.

but couldn't ToInt32 be implemented to accept the comma just like ToDouble function?

I told you before. Convert.ToInt32 doesn't have an overload takes NumberStyles as a parameter. You can use Int32.Parse(String, NumberStyles, IFormatProvider) overload which you can specify your NumberStyles enumeration for parsing your decimal separator or thousands separator.

Upvotes: 3

David Heffernan
David Heffernan

Reputation: 613013

For the double conversion, there are two possibilities:

  1. In your culture, , is the number group separator. And so the conversion succeeds and returns a value of 1000.
  2. Alternatively, in your culture, , is used as the decimal separator. Again the conversion to floating point succeeds but this time returns 1.

For conversion to integer, "1,000" is simply not an integer. My suspicion, given your naming, is that , is a number group separator for you. And you are expecting it to be treated that way by ToInt32(). But ToInt32() does not accept number group separators. Valid characters for ToInt32() are 0 to 9, the optional sign prefix of - or + and leading or trailing whitespace.

Upvotes: 4

Lucian
Lucian

Reputation: 3554

In the english culture the decimal sign is "." . In the swedish culture the decimal sigh is ",". The myriad sign can be " ", "," or ".". So that's why C# throws an exception when the decimal sign is different than its culture specified.

Upvotes: 0

Related Questions