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));
Related
... 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.
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.
I want a price to be a decimal and for the decimal-sign to be localised. Depending on culture, a decimal may use , or ..
For now I am doing this:
subItem.price.ToString("#.#", CultureInfo.InvariantCulture);
But if the price is 0 (with no decimal point), the result of that turns out to be "" and that makes my system crash.
Is there a better pattern than "#.#" that might handle the 0 in a better way?
How about using "0" custom format specifier instead?
Console.WriteLine((0).ToString("0.0", CultureInfo.InvariantCulture));
prints
0.0
Your code returns empty string because from The "#" custom format specifier
Replaces the "#" symbol with the corresponding digit if one is
present; otherwise, no digit appears in the result string.
Note that this specifier never displays a zero that is not a
significant digit, even if zero is the only digit in the string. It
will display zero only if it is a significant digit in the number that
is being displayed.
By the way your title and your question says different things. Your question doesn't relevant with what number decimal separator is used when you format your value. It is all about why you get empty string when you format your 0 value.
Try 0.#. Will display 0 if value is 0.0
Possibly try using:
subItem.price.ToString("{0:C}", CultureInfo.InvariantCulture);
it formats your string as a currency.
So always add a comma and have it to two decimal places, "F" nearly works but can't find the right solution
decimal = 1000.5
test.Text = decimal.ToString("F")
I've also tried:
String.Format("{0:#,###.##}", decimal);
I want to display as the string as 1,000.50
Try:
String.Format("{0:#,###.00}", decimalNumber);
See: Custom Numeric Format Strings
0 - Zero placeholder Replaces the zero with the corresponding digit if
one is present; otherwise, zero appears in the result string. More
information: The "0" Custom Specifier.
It is not going to round the numbers, it is just string formatting.
For culture insensitive formatting do:
String.Format(CultureInfo.InvariantCulture, "{0:#,###.00}", decimalNmber);
String.Format(CultureInfo.InvariantCulture, "{0:0,0.00}", decimal)
For more options see this link.
And here you can test this online.
IF i have the following:
MyString.ValueType = typeof(System.Decimal);
how can i make this have an output of decimals with commas? In other words, instead of seeing 1234.5, i'd like to see 1,234.5
Use:
String output = MyString.ValueType.ToString("N");
The "N" format specifier will put in the thousands separators (,). For details, see Decimal.ToString(string).
Edit:
The above will use your current culture settings, so the thousands separators will depend on the current locale. However, if you want it to always use comma, and period for the decimal separator, you can do:
String output = MyString.ValueType.ToString("N", CultureInfo.InvariantCulture);
That will force it to use the InvariantCulture, which uses comma for thousands and period for decimal separation, which means you'll always see "1,234.5".