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.
Related
This question already has answers here:
String to decimal conversion: dot separation instead of comma
(8 answers)
Closed 3 years ago.
I have this small piece of code:
using System;
using System.Globalization;
namespace project
{
class conditionalStatements
{
static void Main(string[] args)
{
Console.WriteLine("Enter a number greater than 45.2");
string answer = Console.ReadLine();
decimal answer_decimal = Convert.ToDecimal(answer);
// decimal answer_decimal = Decimal.Parse(answer);
decimal compareValue = 45.2m;
Console.WriteLine(answer_decimal);
//prints 452
if(Decimal.Compare(answer_decimal, compareValue) > 0){
// stuff
}
else{
// should enter here
}
}
}
}
The problem is that since both the method Convert.ToDecimal() and Decimal.Parse() ignore the dot notation of decimal values (or at least that's what's happening to me) the number is interpreted as 452 instead of 45.2. No matter how many dots I input. In fact, if I were to enter:
45......2
the converted value still would be converted to 452. Only if I use the comma, then the converted number is correctly interpreted as 45.2 and I am able to enter the else condition.
I did not change the NumberFormatInfo.CurrencyDecimalSeparator. I left it as default '.'
Convert input string to decimal with a given culture that treats dot as decimal separator:
decimal answer_decimal = Convert.ToDecimal(answer, new CultureInfo("en-US"));
You could try something like the code below:
decimal answer_decimal;
while(!decimal.TryParse(answer, out answer_decimal)){
Console.WriteLine("Value entered could not be converted.");
}
decimal compareValue = 45.2m;
Console.WriteLine(answer_decimal);
//prints 452
if(Decimal.Compare(answer_decimal, compareValue) > 0){
// stuff
}
else{
// should enter here
}
This way you prevent the program from crashing if the conversion is not possible.
If you want to use a specific culture you can do so by using an overload of decimal.TryParse as follows:
decimal.TryParse(answer, System.Globalization.NumberStyles.Any, new System.Globalization.CultureInfo("EN-us"), out answer_decimal);
Is your culture set to Dari?
I think the applicable NumberFormatInfo property would be NumberDecimalSeparator, not CurrencyDecimalSeparator. decimal.Parse(), called directly or via Convert.ToDecimal(), would have no idea currency is what's being parsed unless a NumberStyles value with one of the *Currency* flags were passed.
When an overload of decimal.Parse() is called that does not accept a NumberStyles parameter it defaults to NumberStyles.Number. This composite style includes the NumberStyles.AllowDecimalPoint style, the documentation for which states (emphasis mine)...
If the NumberStyles value includes the AllowCurrencySymbol flag and the parsed string includes a currency symbol, the decimal separator character is determined by the CurrencyDecimalSeparator property. Otherwise, the decimal separator character is determined by the NumberDecimalSeparator property.
Now, are there actually any cultures that use different decimal separators for numbers and currency? Let's find out...
CultureInfo.GetCultures(CultureTypes.AllCultures)
.Where(culture => culture.NumberFormat.CurrencyDecimalSeparator != culture.NumberFormat.NumberDecimalSeparator);
On .NET Framework v4.7.2 that yields a small number of cultures...
fr-CH
kea
kea-CV
mr
mr-IN
prs
prs-AF
pt-CV
Tweaking that LINQ query to account for the specific behavior you're seeing (currency decimal separator is ".", number decimal separator is ",", number group separator is ".")...
CultureInfo.GetCultures(CultureTypes.AllCultures)
.Where(culture => culture.NumberFormat.CurrencyDecimalSeparator == ".")
.Where(culture => culture.NumberFormat.NumberDecimalSeparator == ",")
.Where(culture => culture.NumberFormat.NumberGroupSeparator == ".");
...narrows it down to two Dari cultures...
prs
prs-AF
Sure enough, if I change my culture to Dari beforehand...
CultureInfo.CurrentCulture = new CultureInfo("prs");
...on my system your code behaves exactly as you described. If you don't want to use your culture's separators the solution, of course, is to specify at the system, thread, or method level a specific or custom culture with the separators you do want.
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
i got string values representing doubles. (f.e. "1.23"). Always with ".".
If you run the code:
double dDouble =0;
string sDemo ="1.23";
double.TryParse(sDemo,out dDouble));
it will return 1.23 to the dDouble var.
If i switch to a different languge with "," as "." .... i get 123.0 as dDouble.
How do I ensure that its always 1.23 ...
double dDouble =0;
string sDemo ="1.23";
double.TryParse(sDemo,System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out dDouble));
but I am unsure, if this will solve the problem permanently.
i just tried
10 years ago I would solve it like this (do not do that):
sDemo = sDemo.Replace(",",".");
Now
// to string
var text = someDouble.ToString(NumberFormatInfo.InvariantInfo);
// and back
var number = double.Parse(text, NumberFormatInfo.InvariantInfo);
Key point is to use same culture. If you convert values for internal use (to example, passing double value as a part of text) - use invariant culture. If, however, you want to display it to the user and then read user input, then still use same culture: if you don't specify culture for ToString, then don't specify it for Parse, and if you did, then do it again.
This is probably dumb but it's giving me a hard time. I need to convert/format a double to string with a mandatory decimal point.
1 => 1.0
0.2423423 => 0.2423423
0.1 => 0.1
1234 => 1234.0
Basically, I want to output all decimals but also make sure the rounded values have the redundant .0 too. I am sure there is a simple way to achieve this.
Use double.ToString("N1"):
double d1 = 1d;
double d2 = 0.2423423d;
double d3 = 0.1d;
double d4 = 1234d;
Console.WriteLine(d1.ToString("N1"));
Console.WriteLine(d2.ToString("N1"));
Console.WriteLine(d3.ToString("N1"));
Console.WriteLine(d4.ToString("N1"));
Demo
Standard Numeric Format Strings
The Numeric ("N") Format Specifier
Update
(1.234).ToString("N1") produces 1.2 and in addition to removing additional decimal digits, it also adds a thousands separator
Well, perhaps you need to implement a custom NumberFormatInfo object which you can derive from the current CultureInfo and use in double.ToString:
var culture = CultureInfo.CurrentCulture;
var customNfi = (NumberFormatInfo)culture.NumberFormat.Clone();
customNfi.NumberDecimalDigits = 1;
customNfi.NumberGroupSeparator = "";
Console.WriteLine(d1.ToString(customNfi));
Note that you need to clone it since it's readonly by default.
Demo
There is not a built in method to append a mandatory .0 to the end of whole numbers with the .ToString() method, as the existing formats will truncate or round based on the number of decimal places you specify.
My suggestion is to just roll your own implementation with an extension method
public static String ToDecmialString(this double source)
{
if ((source % 1) == 0)
return source.ToString("f1");
else
return source.ToString();
}
And the usage:
double d1 = 1;
double d2 = 0.2423423;
double d3 = 0.1;
double d4 = 1234;
Console.WriteLine(d1.ToDecimalString());
Console.WriteLine(d2.ToDecimalString());
Console.WriteLine(d3.ToDecimalString());
Console.WriteLine(d4.ToDecimalString());
Results in this output:
1.0
0.2423423
0.1
1234.0
You could do something like this: if the number doesn't have decimal points you can format its output to enforce one decimal 0 and if it has decimal places, just use ToString();
double a1 = 1;
double a2 = 0.2423423;
string result = string.Empty;
if(a1 - Math.Floor(a1) >0.0)
result = a1.ToString();
else
result = a1.ToString("F1");
if (a2 - Math.Floor(a2) > 0.0)
result = a2.ToString();
else
result = a2.ToString("F1");
When you use "F" as formatting, the output won't contain thousands separator and the number that follows it specifies the number of decimal places.
Use ToString("0.0###########################").
It does work. I found it in duplicate of your question decimal ToString formatting which gives at least 1 digit, no upper limit
Double provides a method ToString() where you can pass an IFormatProvider-object stating how you want your double to be converted.
Additionally, it should display trailing 0 at all costs.
value = 16034.125E21;
// Display value using the invariant culture.
Console.WriteLine(value.ToString(CultureInfo.InvariantCulture));
// Display value using the en-GB culture.
Console.WriteLine(value.ToString(CultureInfo.CreateSpecificCulture("en-GB")));
// Display value using the de-DE culture.
Console.WriteLine(value.ToString(CultureInfo.CreateSpecificCulture("de-DE")));
// This example displays the following output to the console:
// -16325.62015
// -16325.62015
// -16325,62015
// 1.6034125E+25
// 1.6034125E+25
// 1,6034125E+25
Here is the documentation from MSDN.
You can cast to string and then appen ".0" if there was no decimal point given
string sValue=doubleValue.ToString();
if(!sValue.Contains('.'))
sValue+=".0";
EDIT:
As mentioned in the comments '.' may not be the decimal seperator in the current culture. Refer to this article to retrieve the actual seperator if you want make your code save for this case.
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.