Why decimal.Parse(10 10) is valid? - c#

Why decimal.Parse(10 10) is valid?
I need to get exception in such case.
Please advise me something.
decimal c;
try
{
c = decimal.Parse("10 10");
Console.Write(c);
Console.ReadLine();
}
catch (Exception)
{
throw;
}

This throws an exception when I run it - which leads me to suspect that it's culture-sensitive.
My guess is that you're in a culture which uses space as a "thousands" separator. For example, if I try to parse "10,10" that works because comma is the thousands separator in my default culture.
To prevent this, use
decimal value = decimal.Parse(text, NumberStyles.None);
... or some other appropriate combination of NumberStyles which excludes AllowThousands.

From MSDN: "Parameter s is interpreted using the NumberStyles.Number style. This means that white space and thousands separators are allowed but currency symbols are not. To explicitly define the elements (such as currency symbols, thousands separators, and white space) that can be present in s, use either the Decimal.Parse(String, NumberStyles) or the Decimal.Parse(String, NumberStyles, IFormatProvider) method."
http://msdn.microsoft.com/en-us/library/cafs243z.aspx
edit: To further clarify, you need to either
explicitly set the culture of your application to one which does NOT allow whitespace in numbers, or
explicitly provide a NumberStyles parameter which specifies that whitespace is NOT allowed
edit 2: Jon Skeet's answer is correct. For example, the following does NOT throw an exception, because whitespace is used as thousands separators in sv-SE:
Decimal.Parse(" 10 10 ", CultureInfo.GetCultureInfo("sv-SE").NumberFormat)
The following, however, DOES throw an exception:
Decimal.Parse(" 10 10 ", CultureInfo.GetCultureInfo("en-US").NumberFormat)

I just ran this code on Visual Studio 2010/C# 4.0, and got a FormatException, as expected. What regional settings is your computer configured to use? Is it possible that you have " " (space) as a thousands separator or decimal separator?

Related

Why does float.Parse("123,45") not throw an exception

... but returns 12345?
The doc for Single.Parse says:
Exceptions
...
FormatException
s does not represent a numeric value.
...
For my understanding "123,45" doesn't represent a proper numeric value (in countries that use comma as thousands separator).
The system's CultureInfo has:
CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == "."
CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator == ","
CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes == [3]
Apparently the comma is simply ignored and this leads to even more irritating results: "123,45.67" or "1,23,45.67"–which look utterly wrong–become 12345.67.
Supplementary question
I don't get what this sentence in the doc is supposed to mean and whether this is relevant for this case:
If a separator is encountered in the s parameter during a parse operation, and the applicable currency or number decimal and group separators are the same, the parse operation assumes that the separator is a decimal separator rather than a group separator.
In the default and US culture, the comma (,) is legal as a separator between groups. Think of larger numbers like this:
987,654,321
That it's in the wrong place for a group doesn't really matter; the parser isn't that smart. It just ignores the separator.
For the supplemental question, some cultures use commas as the decimal separator, rather than a group separator. This part of the documentation clarifies what will happen if the group separator and decimal separator are somehow set to the same character.
As Joel said, "the parser isn't that smart". The source code is available, so here's the proof.
The code for Single.Parse ends up calling Number.ParseNumber.
Interestingly, Number.ParseNumber is given a NumberFormatInfo object, which does have a NumberGroupSizes property, which defines "the number of digits in each group to the left of the decimal".
However, you'll notice that on line 851, where it checks for the group separator, it doesn't bother to reference the NumberGroupSizes property to check if the group separator is in an expected position. In fact Number.ParseNumber never uses the NumberGroupSizes property.
NumberFormatInfo.NumberGroupSizes is only ever used when converting a number to a string.

Int32.Parse vs Single.Parse - ("1,234") and ("1,2,3,4"). Why do int and floating point types parse separator chars differently?

In C#:
This throws a FormatException, which seems like it shouldn't:
Int32.Parse("1,234");
This does not, which seems normal:
Single.Parse("1,234");
And surprisingly, this parses just fine:
Single.Parse("1,2,3,4"); //Returns 1234
My local culture is EN-US, so , is the default thousands separator char.
Main question: Why the inconsistency?
Also: Why does Parse("1,2,3,4") work? It appears to just be removing all instances of the local separator char before parsing. I know there would be extra runtime overhead in a regex check or something like that, but when would the numeric literal "1,2,3,4" not be a typo?
Related:
C# Decimal.Parse issue with commas
According to MSDN:
The s parameter contains a number of the form:
[ws][sign]digits[ws]
The s parameter is interpreted using the NumberStyles.Integer style. In addition to decimal digits, only leading and trailing spaces together with a leading sign are allowed.
That's it, NumberStyles.Integer disallows the Parse method to use the thousands separator, whereas Single.Parse uses by default NumberStyles.Float and NumberStyles.AllowThousands. You can change this behaviour by specifiying the second argument as NumberStyles:
Int32.Parse("1,234", NumberStyles.AllowThousands); //works
Single.Parse ignores the grouping and doesn't use culture-specific NumberGroupSizes at all, and only determines if the character is a group or decimal separator. The group sizes are used only when formatting numbers.
For the first case, from Microsoft Source Code Reference, by default Int32.Parse implements NumberStyles.Integer but not NumberStyles.AllowThousands
public static int Parse(String s) {
return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
Thus any comma separator is not allowed. This:
Int32.Parse("1,234");
or
Int32.Parse("1.234");
will both be wrong. In any culture.
To fix it, NumberStyles.AllowThousands must be added to the NumberStyles which will allow "1,234" to be parsed in EN-US culture:
Int32.Parse("1,234", NumberStyles.Integer | NumberStyles.AllowThousands);
But
Int32.Parse("1.234", NumberStyles.Integer | NumberStyles.AllowThousands);
Will still throw an Exception.
For the second case, according to Microsoft Code Source Reference, the default style for Single.Parse is:
public static float Parse(String s) {
return Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo);
}
Which allows thousands separator. And "," is recognized as thousand separator in EN-US culture, for Single.Parse and thus you get the second case parsed correctly
Single.Parse("1,234"); //OK
And obviously "1.234" will also be correct, except that "." is not recognized as thousand separators but decimal separator.
As for the third case, Internally, Single.Parse calls TryStringToNumber, and Parse.Number which would simply ignore the thousand separators. Thus you get:
Single.Parse("1,2,3,4"); //Returns 1234
Because it is equivalent as
Single.Parse("1234"); //Returns 1234
mine is es-ES
On these . is the default thousands separator char, and "," the separate character between int and double
So
any parse like "1.2.3,4" gives me "123,40" ( 123.40 on US )
If i put the "." before the "," like "123,4.3" it gives error
but, the same way the questions says, if i put "1.2.3.4" gives me "1234"
So, may be it is a functionality of the .net itself.

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.
For the double conversion, there are two possibilities:
In your culture, , is the number group separator. And so the conversion succeeds and returns a value of 1000.
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.
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 ,
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.
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.

Why string.Format produces such result?

I have a line like the following in my code:
string buffer = string.Format(CultureInfo.InvariantCulture, "{0:N4}", 1008.0);
Why does buffer contain 1,008.0 (note comma) after executing this line?
Yes, I do guess that it's caused by my regional settings. The question is why they affect the result in this case?
EDIT:
Ok, I understand that it's completely my fault. It seems like I should have used F format specifier.
The InvariantCulture is loosely based on en-US which uses , as a thousands (group) separator.
Your result is what I would expect.
I also point you to the details of the N numeric format specifier:
The numeric ("N") format specifier converts a number to a string of the form "-d,ddd,ddd.ddd…", where "-" indicates a negative number symbol if required, "d" indicates a digit (0-9), "," indicates a group separator, and "." indicates a decimal point symbol.
You're using the invariant culture; your culture is irrelevant to this. For this, the N4 format means
-d,ddd,ddd,ddd...
That is, possible leading negative sign indicator and commas between thousands groups. For details see: http://msdn.microsoft.com/en-us/library/dwhawy9k#NFormatString
You can look at
NegativeSign
NumberNegativePattern
NumberGroupSizes
NumberGroupSeparator
NumberDecimalSeparator
NumberDecimalDigits
for the invariant culture. If you do, you'll see:
-
1
{ 3 }
,
.
2
You are getting the comma because of "{0:N4}"
n ----- Number with commas for thousands ----- {0:n}
Source:
You will get the comma even without specifying InvariantCulture
Console.WriteLine(string.Format("{0:n4}", 1008.0));

DateTime: how to display as DD.MM.YYYY?

I've got DateTime variable and i want to convert it to string "DD.MM.YYYY"
Please note, the values must be separated by "dot" sign.
Of course I can do manual string composition. But I wonder if I can use DateTime.ToString()
to do required conversion.
Yes, you can:
string formatted = dt.ToString("dd'.'MM'.'yyyy");
Now in this case the quotes aren't actually required, as custom date/time format strings don't interpret dot in any special way. However, I like to make it explicit - if change '.' for ':' for example, then while it's quoted it will stay with the explicit character, but unquoted it would be "the culture-specific time separator". It wasn't entirely obvious to me whether "." would be interpreted as "the culture-specific decimal separator" or not, hence the quoting. You may feel that's over the top, of course - it's entirely your decision.
You may also want to specify the invariant culture, just to remove any other traces of doubt:
string formatted = dt.ToString("dd'.'MM'.'yyyy", CultureInfo.InvariantCulture);
(At that point the quotes around the dot become less relevant, as "." is the decimal separator in the invariant culture anyway.)
Yes, you can use DateTime.ToString like this:
myDateVariable.ToString("dd.MM.yyyy");
Note that you have to use capital MM here, since mm evaluates to minutes instead of months.
Here's an alternative for you:
DateTime.Now.ToString("d", new CultureInfo("de-DE"))
German's use . as the date separator.
You can format the date like this:
date.ToString("dd.MM.yyyy")
When formatting numbers the period . will change depending on the CultureInfo used, but not when formatting dates.
If you are verifying your code against the code analysis rule CA1304: Specify CultureInfo you will have to use the invariant culture even though it doesn't matter for this particular format:
date.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture)

Categories

Resources