In my application I have internationalization and so we have a bunch of methods to deal with formatting.
One of them should receive a double and format it to two decimal places and return a double. For doing so, we are using NumberFormatInfo according to the culture selected.
The problem is I cant get Convert.ToDouble to work with NumberFormatInfo the way I would like to. Basically what I want to know is why this:
using System;
using System.Globalization;
public class Program
{
public static void Main()
{
var myDouble = 9.983743;
var nfi = new NumberFormatInfo() {
NumberDecimalDigits = 2
};
Console.WriteLine("Original value: " + myDouble);
Console.WriteLine("Converted value: " + Convert.ToDouble(myDouble, nfi));
}
}
Prints
Original value: 9.983743
Converted value: 9.983743 // Should be 9.98
And how can I get the result I want using NumberFormatInfo only, if possible.
Thanks,
From MSDN:
The NumberDecimalDigits property is used with the "F" and "N" standard format strings without a precision specifier in numeric formatting operations.
The default is the generic formatting (G). So this will give you the desired result:
Console.WriteLine(myDouble.ToString("N", nfi));
However, 2 is the default value anyway. And it is better to specify it explicitly:
Console.WriteLine(myDouble.ToString("N2", NumberFormatInfo.InvariantInfo));
Update:
Yeah but I do need to return a double from my method.
Now I see. In your place I would return the original double in that case, too. If the consumer of your API wants to display/store it as a string with two digits, then it is his responsibility to format it.
If you really want to omit the last digits of the precision and return a modified value, then use Math.Round instead (but I would not recommend that).
Converting a double to a double does not change anything, a double is always double precision, you cannot change the number of decimal digits.
A double has no format, format comes into play when you display a double by using the ToString() method as explained by taffer.
You can round it down to set all digits after the first 2 to zero, but you cannot remove digits.
You state you want to return a double with only 2 digits after the decimal place. Therefore, you are not formatting you are rounding:
Math.Round(myDouble, 2)
9.98
Background: We have a system that receives data from another backend system. We handle the displaying of that data, and we have our own XML templates to control how certain things are displayed (i.e. we have our own column templates to dictate what the column headers are, etc.) One thing we would like to support is the ability to provide a mask for these column templates that would apply to the values coming from the backend. Below is a scenario that I'm having trouble with.
Problem: I can't seem to get a simple string format working. I'd like to format a STRING value of four digits (i.e. "1444") in a time format (i.e. "14:44"). I've tried:
String.Format("{0:00:00}", "1444")
Note the importance of the input being a STRING. If I supply an int value, the format will work. The reason I cannot use this is because all the data we receive from the backend is in string format, and we'd like for this to be generic (so casting isn't really an option).
By generic, I mean I'd like to specify a mask in our own XML templates, something like:
<MyColumnTemplate Id="MyColumn" Mask="00:00" />
and to use that mask in a string format call for string values? If the mask fails, we could just simply return the original value (as the String.Format() method already does by default).
Edit: To help clarify, here is a simplified version of what I'd like to be able to do in code:
string inputValue = "1444";
string maskValueFromXml = "00:00";
string mask = "{0:" + maskValueFromXml + "}";
string myDesiredEndResult = String.Format(mask, inputValue);
The thing is you are working string to string,since you ask for time and phone number they are all numbers then try this trick(if we can call it that :)):
string result = string.Format("{0:00:00}", int.Parse("1444"));
For phone number:
string result = string.Format("{0:000-000-0000}", int.Parse("1234560789"));
You can even place your desired masks in a dictionary for example and do this:
Dictionary<string, string> masks = new Dictionary<string, string>();
masks.Add("Phone", "{0:000-000-0000}");
masks.Add("Time", "{0:00:00}");
string test = "1234560789";
string result = string.Format(masks["Phone"], int.Parse(test));
Try with DateTime.TryParseExact, e.g:
DateTime dateEntered;
string input = "1444";
if (DateTime.TryParseExact(input, "HH:mm", System.Globalization.CultureInfo.CurrentCulture, System.Globalization.DateTimeStyles.None, out dateEntered))
{
MessageBox.Show(dateEntered.ToString());
}
else
{
MessageBox.Show("You need to enter valid 24hr time");
}
After that, you can use string.Format, predefined formats on MSDN.
This might be a simple and basic question, but, thought of confirming with you. Im in the process of writing code to validate the entered text information is double or not. In my code, below is the line to validate the speed value taken from a text box.
double _mSpeed = 0.0;
if (!Double.TryParse(txtboxSpeed.Text, out _mSpeed))
throw new Exception("Input value for Speed is invalid !!!");
But, if user provides 4.4.4 in speed text box, TryParse is parsing the text string to 444.0 value. Im wondering this is correct or not. Please share your thoughts whether if user enters any value with 2 decimal points, should it not parse to double or what is the expected behavior.
In cultures where . is the decimal separator, like en-US and the invariant culture, 4.4.4 is not valid. In other cultures, like de-DE, , is the decimal separator and . is the thousands separator, so 4.4.4 is 444 (with nonstandard, but acceptable, thousands separators inserted, like 4,4,4 in the en-US culture).
double.Parse("4.4.4", new CultureInfo("de-DE")) // 444
double.Parse("4.4.4", new CultureInfo("en-US")) // FormatException: Input string was not in a correct format.
double.Parse("4,4,4", new CultureInfo("en-US")) // 444
double.Parse("4,4,4", new CultureInfo("de-DE")) // FormatException: Input string was not in a correct format.
The issue could be that the Culture currently used by your application treats comma as decimal separator instead of period. You can force it to use period as decimal separator by setting culture to en-GB.
double _mSpeed = 0.0;
if (!Double.TryParse(txtboxSpeed.Text,NumberStyles.Any,CultureInfo.CreateSpecificCulture("en-GB"), out _mSpeed))
throw new Exception("Input value for Speed is invalid !!!");
Instead of creating a CultureInfo for a specific culture that uses your number format (e.g., "en-US"), you can also just specify NumberFormatInfo.InvariantInfo.
double val1, val2;
bool b1 = double.TryParse("4.4.4", NumberStyles.Any, NumberFormatInfo.InvariantInfo, out val1);
bool b2 = double.TryParse("4,444.4", NumberStyles.Any, NumberFormatInfo.InvariantInfo, out val2);
In the above code b1 is set to false but b2 succeeds and val2 is 4444.4.
Consider a Decimal value:
Decimal value = -1234567890.1234789012M;
i want to convert this Decimal value to a string, and include "thousands separators".
Note: i don't want to include thousand's separators, i want to include digit grouping. The difference is important for cultures that don't group numbers into thousands, or don't use commas to separate groups
Some example output with different standard formatting strings, on my computer, with my current locale:
value.ToString() = -1234567890..1234789012 (Implicit General)
value.ToString("g") = -1234567890..1234789012 (General)
value.ToString("d") = FormatException (Decimal whole number)
value.ToString("e") = -1..234568e++009 (Scientific)
value.ToString("f") = -1234567890..123 (Fixed Point)
value.ToString("n") = -12,,3456,,7890..123 (Number with commas for thousands)
value.ToString("r") = FormatException (Round trippable)
value.ToString("c") = -$$12,,3456,,7890..123 (Currency)
value.ToString("#,0.#") = -12,,3456,,7890..1
What i want (depending on culture) is:
en-US -1,234,567,890.1234789012
ca-ES -1.234.567.890,1234789012
gsw-FR -1 234 567 890,1234789012 (12/1/2012: fixed gws-FR to gsw-FR)
fr-CH -1'234'567'890.1234789012
ar-DZ 1,234,567,890.1234789012-
prs-AF 1.234.567.890,1234789012-
ps-AF 1،234،567،890,1234789012-
as-IN -1,23,45,67,890.1234789012
lo-LA (1234567,890.1234789012) (some debate if numbers should be "1,234,567,890")
qps-PLOC 12,,3456,,7890..1234789012
How can i convert a Decimal to a string, with digit groupings?
Update: Some more desired output, using my current culture of :
-1234567890M --> -12,,3456,,7890
-1234567890.1M --> -12,,3456,,7890..1
-1234567890.12M --> -12,,3456,,7890..12
-1234567890.123M --> -12,,3456,,7890..123
-1234567890.1234M --> -12,,3456,,7890..1234
-1234567890.12347M --> -12,,3456,,7890..12347
-1234567890.123478M --> -12,,3456,,7890..123478
-1234567890.1234789M --> -12,,3456,,7890..1234789
-1234567890.12347890M --> -12,,3456,,7890..1234789
-1234567890.123478901M --> -12,,3456,,7890..123478901
-1234567890.1234789012M --> -12,,3456,,7890..1234789012
Update: i tried peeking at how Decimal.ToString() manages to use the General format to show all the digits that it needs to show:
public override string ToString()
{
return Number.FormatDecimal(this, null, NumberFormatInfo.CurrentInfo);
}
except that Number.FormatDecimal is hidden somewhere:
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern string FormatDecimal(decimal value, string format, NumberFormatInfo info);
So that's a dead end.
The ToString method on decimals by default uses the CultureInfo.CurrentCulture for the user's session, and thus varies based on whom is running the code.
The ToString method also accepts an IFormatProvider in various overloads. This is where you need to supply your culture-specific Formatters.
For instance, if you pass the NumberFormat for fr-CH, you can format things as that culture expects:
var culture = CultureInfo.CreateSpecificCulture("fr-CH");
Decimal value = -1234567890.1234789012M;
Console.WriteLine(value.ToString("##,#.###############", culture.NumberFormat));
Will output
-1'234'567'890.1234789012
Edit #3 - rewrote using custom formatters. This should do what you want based on the new updated question.
Edit #4 - Took all of your input, and ran this:
public void TestOutput()
{
PrintValue(-1234567890M);
PrintValue(-1234567890.1M);
PrintValue(-1234567890.12M);
PrintValue(-1234567890.123M);
PrintValue(-1234567890.1234M);
PrintValue(-1234567890.12347M);
PrintValue(-1234567890.123478M);
PrintValue(-1234567890.1234789M);
PrintValue(-1234567890.12347890M);
PrintValue(-1234567890.123478901M);
PrintValue(-1234567890.1234789012M);
}
private static void PrintValue(decimal value)
{
var culture = CultureInfo.CreateSpecificCulture("qps-PLOC");
Console.WriteLine(value.ToString("##,#.###############", culture.NumberFormat));
}
Gives output matching what you supplied:
--12,,3456,,7890
--12,,3456,,7890..1
--12,,3456,,7890..12
--12,,3456,,7890..123
--12,,3456,,7890..1234
--12,,3456,,7890..12347
--12,,3456,,7890..123478
--12,,3456,,7890..1234789
--12,,3456,,7890..1234789
--12,,3456,,7890..123478901
--12,,3456,,7890..1234789012
As pointed out by Joshua, this only works for some locales.
From the looks of it then, you need to pick the lesser of two evils: Knowing the precision of your numbers, or specifying formats for each culture. I'd wager knowing the precision of your numbers may be easier.
In which case, a previous version of my answer may be of use:
To explicitly control the number of decimal places to output, you can clone the number format provided by the culture and modify the NumberDecimalDigits property.
var culture = CultureInfo.CreateSpecificCulture("fr-CH");
Decimal value = -1234567890.1234789012M;
NumberFormatInfo format = (NumberFormatInfo)culture.NumberFormat.Clone();
format.NumberDecimalDigits = 30;
Console.WriteLine(value.ToString("n", format));
This outputs:
-1'234'567'890.123478901200000000000000000000
You can specify a custom pattern (the pattern will appropriately resolve to the culture specific method of grouping and the appropriate grouping and decimal separator characters). A pattern can have positive, negative and zero sections. The positive pattern is always the same but the negative pattern depends on the culture and can be retrieved from the NumberFormatInfo's NumberNegativePattern property. Since you want as much precision as possible, you need to fill out 28 digit placeholders after the decimal; the comma forces grouping.
public static class DecimalFormatters
{
public static string ToStringNoTruncation(this Decimal n, IFormatProvider format)
{
NumberFormatInfo nfi = NumberFormatInfo.GetInstance(format);
string[] numberNegativePatterns = {
"(#,0.############################)", //0: (n)
"-#,0.############################", //1: -n
"- #,0.############################", //2: - n
"#,0.############################-", //3: n-
"#,0.############################ -"};//4: n -
var pattern = "#,0.############################;" + numberNegativePatterns[nfi.NumberNegativePattern];
return n.ToString(pattern, format);
}
public static string ToStringNoTruncation(this Decimal n)
{
return n.ToStringNoTruncation(CultureInfo.CurrentCulture);
}
}
Sample output
Locale Output
======== ============================
en-US -1,234,567,890.1234789012
ca-ES -1.234.567.890,1234789012
hr-HR - 1.234.567.890,1234789012
gsw-FR -1 234 567 890,1234789012
fr-CH -1'234'567'890.1234789012
ar-DZ 1,234,567,890.1234789012-
prs-AF 1.234.567.890,1234789012-
ps-AF 1،234،567،890,1234789012-
as-IN -1,23,45,67,890.1234789012
lo-LA (1234567,890.1234789012)
qps-PLOC -12,,3456,,7890..1234789012
There is currently no locale that uses NegativeNumberFormat 4 (n -), so that case cannot be tested. But there's no reason to think it would fail.
You need to include the culture when formatting for your strings. You can either use String.Format and include the culture as the first parameter or use the object's ToString method and use the overload that takes a culture.
The following code produces the expected output (except for gws-FR, it couldn't find a culture with that string).
namespace CultureFormatting {
using System;
using System.Globalization;
class Program {
public static void Main() {
Decimal value = -1234567890.1234789012M;
Print("en-US", value);
Print("ca-ES", value);
//print("gws-FR", value);
Print("fr-CH", value);
Print("ar-DZ", value);
Print("prs-AF", value);
Print("ps-AF", value);
Print("as-IN", value);
Print("lo-LA", value);
Print("qps-PLOC", value);
}
static void Print(string cultureName, Decimal value) {
CultureInfo cultureInfo = new CultureInfo(cultureName);
cultureInfo.NumberFormat.NumberDecimalDigits = 10;
// Or, you could replace the {1:N} with {1:N10} to do the same
// for just this string format call.
string result =
String.Format(cultureInfo, "{0,-8} {1:N}", cultureName, value);
Console.WriteLine(result);
}
}
}
The above code produces the following output:
en-US -1,234,567,890.1234789012
ca-ES -1.234.567.890,1234789012
fr-CH -1'234'567'890.1234789012
ar-DZ 1,234,567,890.1234789012-
prs-AF 1.234.567.890,1234789012-
ps-AF 1،234،567،890,1234789012-
as-IN -1,23,45,67,890.1234789012
lo-LA (1234567,890.1234789012)
qps-PLOC --12,,3456,,7890..1234789012
If you're working with a multithreaded system, such as ASP.Net, you can change the thread's CurrentCulture property. Changing the thread's culture will allow all of the associated ToString and String.Format calls to use that culture.
Update
Since you're wanting to display all of the precision you're going to have to do a bit of work. Using NumberFormat.NumberDecimalDigits will work, except that if the value has less precision, the number will output with trailing zeros. If you need to make sure you display every digit without any extras, you will need to calculate the precision beforehand and set that before you convert it to a string. The StackOverflow question Calculate System.Decimal Precision and Scale may be able to help you determine the precision of the decimal.
Is it possible to format a double, so he doesn't chance the text 2140.76 to 214076 but instead letting it be 2140.76?
I can't use ',' for the decimal numbers, since the entire text file that I'm reading are numbers using '.' for separating the decimals, 10000 records, every day, so ...
EDIT:
double natMB = 0;
boolean check = double.TryParse(splitline[8], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out natMB);
if (check == false)
{
natMB = 0;
}
else
{
natMB = natMB * 1024;
}
double intMB = 0;
boolean check2 = double.TryParse(splitline[9], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out intMB);
if (check2==false)
{
intMB=0;
}
else
{
intMB = intMB * 1024;
}
The 0 value is necessary since I need to enter these values in an SQL statement, and they need to show up as 0, not as null.
Your question is not clear, Do you want to parse a double from a string with dot decimal separator ?
If yes try with this :
double.Parse("2140.76", CultureInfo.InvariantCulture)
You can use the invariant culture to format a number with a decimal period, regardless of your local culture settings:
string formatted = someDouble.ToString(CultureInfo.InvariantCulture);
Start reading here:
http://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.aspx
Basically, you create an NumberFormatInfo that you can customise to use with String.Format to use any format you want.
Is it possible to format a double so he doesn't chance the text 2140.76 to 214076 but instead
letting it be 2140.76?
Yes. Let me play ignorant - I have no idea how you can even ask that and have the poroblem given the extensive formatting methods.
I can't use ',' for the decimal numbers, since the entire text file that i'm reading are numbers
using '.' for separating the decimals, 10000 records, every day, so ...
So the problem likely is that you ahve a culture issue at hand and an ignorant developer on the other side. Ignorant because files exchagned should always be english formatted always, to avoid that.
Anyhow, basically:
* Change your culture info in the thread to english or
* Use english culture info for parsing and text generation.
Read the documentation for the methods you use -there areo verlaods that allow you to tune the formatting.