How do I parse a string with a decimal point to a double?

C#StringParsingDouble

C# Problem Overview


I want to parse a string like "3.5" to a double. However,

double.Parse("3.5") 

yields 35 and

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint) 

throws a FormatException.

Now my computer's locale is set to German, wherein a comma is used as decimal separator. It might have to do something with that and double.Parse() expecting "3,5" as input, but I'm not sure.

How can I parse a string containing a decimal number that may or may not be formatted as specified in my current locale?

C# Solutions


Solution 1 - C#

double.Parse("3.5", CultureInfo.InvariantCulture)

Solution 2 - C#

I usualy use a multi-culture function to parse user input, mostly because if someone is used to the numpad and is using a culture that use a comma as the decimal separator, that person will use the point of the numpad instead of a comma.

public static double GetDouble(string value, double defaultValue)
{
    double result;

    //Try parsing in the current culture
    if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
        //Then try in US english
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
        //Then in neutral language
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
    {
        result = defaultValue;
    }

    return result;
}

Beware though, @nikie comments are true. To my defense, I use this function in a controlled environment where I know that the culture can either be en-US, en-CA or fr-CA. I use this function because in French, we use the comma as a decimal separator, but anybody who ever worked in finance will always use the decimal separator on the numpad, but this is a point, not a comma. So even in the fr-CA culture, I need to parse number that will have a point as the decimal separator.

Solution 3 - C#

I couldn't write a comment, so I write here:

double.Parse("3.5", CultureInfo.InvariantCulture) is not a good idea, because in Canada we write 3,5 instead of 3.5 and this function gives us 35 as a result.

I tested both on my computer:

double.Parse("3.5", CultureInfo.InvariantCulture) --> 3.5 OK
double.Parse("3,5", CultureInfo.InvariantCulture) --> 35 not OK

This is a correct way that Pierre-Alain Vigeant mentioned

public static double GetDouble(string value, double defaultValue)
{
    double result;

    // Try parsing in the current culture
    if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
        // Then try in US english
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
        // Then in neutral language
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
    {
        result = defaultValue;
    }
    return result;
}

Solution 4 - C#

Double.Parse("3,5".Replace(',', '.'), CultureInfo.InvariantCulture)

Replace the comma with a point before parsing. Useful in countries with a comma as decimal separator. Think about limiting user input (if necessary) to one comma or point.

Solution 5 - C#

Look, every answer above that proposes writing a string replacement by a constant string can only be wrong. Why? Because you don't respect the region settings of Windows! Windows assures the user to have the freedom to set whatever separator character s/he wants. S/He can open up the control panel, go into the region panel, click on advanced and change the character at any time. Even during your program run. Think of this. A good solution must be aware of this.

So, first you will have to ask yourself, where this number is coming from, that you want to parse. If it's coming from input in the .NET Framework no problem, because it will be in the same format. But maybe it was coming from outside, maybe from a external server, maybe from an old DB that only supports string properties. There, the db admin should have given a rule in which format the numbers are to be stored. If you know for example that it will be an US DB with US format you can use this piece of code:

CultureInfo usCulture = new CultureInfo("en-US");
NumberFormatInfo dbNumberFormat = usCulture.NumberFormat;
decimal number = decimal.Parse(db.numberString, dbNumberFormat);

This will work fine anywhere on the world. And please don't use 'Convert.ToXxxx'. The 'Convert' class is thought only as a base for conversions in any direction. Besides: You may use the similar mechanism for DateTimes too.

Solution 6 - C#

The trick is to use invariant culture, to parse dot in all cultures.

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.NumberFormatInfo.InvariantInfo);

Solution 7 - C#

string testString1 = "2,457";
string testString2 = "2.457";    
double testNum = 0.5;
char decimalSepparator;
decimalSepparator = testNum.ToString()[1];
    
Console.WriteLine(double.Parse(testString1.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));
Console.WriteLine(double.Parse(testString2.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));

Solution 8 - C#

My two cents on this topic, trying to provide a generic, double conversion method:

private static double ParseDouble(object value)
{
    double result;

    string doubleAsString = value.ToString();
    IEnumerable<char> doubleAsCharList = doubleAsString.ToList();

    if (doubleAsCharList.Where(ch => ch == '.' || ch == ',').Count() <= 1)
    {
        double.TryParse(doubleAsString.Replace(',', '.'),
            System.Globalization.NumberStyles.Any,
            CultureInfo.InvariantCulture,
            out result);
    }
    else
    {
        if (doubleAsCharList.Where(ch => ch == '.').Count() <= 1
            && doubleAsCharList.Where(ch => ch == ',').Count() > 1)
        {
            double.TryParse(doubleAsString.Replace(",", string.Empty),
                System.Globalization.NumberStyles.Any,
                CultureInfo.InvariantCulture,
                out result);
        }
        else if (doubleAsCharList.Where(ch => ch == ',').Count() <= 1
            && doubleAsCharList.Where(ch => ch == '.').Count() > 1)
        {
            double.TryParse(doubleAsString.Replace(".", string.Empty).Replace(',', '.'),
                System.Globalization.NumberStyles.Any,
                CultureInfo.InvariantCulture,
                out result);
        }
        else
        {
            throw new ParsingException($"Error parsing {doubleAsString} as double, try removing thousand separators (if any)");
        }
    }

    return result;
}

Works as expected with:

  • 1.1
  • 1,1
  • 1,000,000,000
  • 1.000.000.000
  • 1,000,000,000.99
  • 1.000.000.000,99
  • 5,000,111.3
  • 5.000.111,3
  • 0.99,000,111,88
  • 0,99.000.111.88

No default conversion is implemented, so it would fail trying to parse 1.3,14, 1,3.14 or similar cases.

Solution 9 - C#

The following code does the job in any scenario. It's a little bit parsing.

List<string> inputs = new List<string>()
{
    "1.234.567,89",
    "1 234 567,89",
    "1 234 567.89",
    "1,234,567.89",
    "123456789",
    "1234567,89",
    "1234567.89",
};
string output;

foreach (string input in inputs)
{
    // Unify string (no spaces, only .)
    output = input.Trim().Replace(" ", "").Replace(",", ".");

    // Split it on points
    string[] split = output.Split('.');

    if (split.Count() > 1)
    {
        // Take all parts except last
        output = string.Join("", split.Take(split.Count()-1).ToArray());

        // Combine token parts with last part
        output = string.Format("{0}.{1}", output, split.Last());
    }

    // Parse double invariant
    double d = double.Parse(output, CultureInfo.InvariantCulture);
    Console.WriteLine(d);
}

Solution 10 - C#

I think 100% correct conversion isn't possible, if the value comes from a user input. e.g. if the value is 123.456, it can be a grouping or it can be a decimal point. If you really need 100% you have to describe your format and throw an exception if it is not correct.

But I improved the code of JanW, so we get a little bit more ahead to the 100%. The idea behind is, that if the last separator is a groupSeperator, this would be more an integer type, than a double.

The added code is in the first if of GetDouble.

void Main()
{
    List<string> inputs = new List<string>() {
        "1.234.567,89",
        "1 234 567,89",
        "1 234 567.89",
        "1,234,567.89",
        "1234567,89",
        "1234567.89",
        "123456789",
        "123.456.789",
        "123,456,789,"
    };

    foreach(string input in inputs) {
        Console.WriteLine(GetDouble(input,0d));
    }

}

public static double GetDouble(string value, double defaultValue) {
    double result;
    string output;

    // Check if last seperator==groupSeperator
    string groupSep = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
    if (value.LastIndexOf(groupSep) + 4 == value.Count())
    {
        bool tryParse = double.TryParse(value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentCulture, out result);
        result = tryParse ? result : defaultValue;
    }
    else
    {
        // Unify string (no spaces, only . )
        output = value.Trim().Replace(" ", string.Empty).Replace(",", ".");

        // Split it on points
        string[] split = output.Split('.');

        if (split.Count() > 1)
        {
            // Take all parts except last
            output = string.Join(string.Empty, split.Take(split.Count()-1).ToArray());

            // Combine token parts with last part
            output = string.Format("{0}.{1}", output, split.Last());
        }
        // Parse double invariant
        result = double.Parse(output, System.Globalization.CultureInfo.InvariantCulture);
    }
    return result;
}

Solution 11 - C#

		var doublePattern = @"(?<integer>[0-9]+)(?:\,|\.)(?<fraction>[0-9]+)";
        var sourceDoubleString = "03444,44426";
        var match = Regex.Match(sourceDoubleString, doublePattern);
        
        var doubleResult = match.Success ? double.Parse(match.Groups["integer"].Value) + (match.Groups["fraction"].Value == null ? 0 : double.Parse(match.Groups["fraction"].Value) / Math.Pow(10, match.Groups["fraction"].Value.Length)): 0;
        Console.WriteLine("Double of string '{0}' is {1}", sourceDoubleString, doubleResult);

Solution 12 - C#

I improved the code of @JanW as well...

I need it to format results from medical instruments, and they also send ">1000", "23.3e02", "350E-02", and "NEGATIVE".

private string FormatResult(string vResult)
{
  string output;
  string input = vResult;

  // Unify string (no spaces, only .)
  output = input.Trim().Replace(" ", "").Replace(",", ".");

  // Split it on points
  string[] split = output.Split('.');

  if (split.Count() > 1)
  {
    // Take all parts except last
    output = string.Join("", split.Take(split.Count() - 1).ToArray());

    // Combine token parts with last part
    output = string.Format("{0}.{1}", output, split.Last());
  }
  string sfirst = output.Substring(0, 1);

  try
  {
    if (sfirst == "<" || sfirst == ">")
    {
      output = output.Replace(sfirst, "");
      double res = Double.Parse(output);
      return String.Format("{1}{0:0.####}", res, sfirst);
    }
    else
    {
      double res = Double.Parse(output);
      return String.Format("{0:0.####}", res);
    }
  }
  catch
  {
    return output;
  }
}

Solution 13 - C#

Instead of having to specify a locale in all parses, I prefer to set an application wide locale, although if string formats are not consistent across the app, this might not work.

CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("pt-PT");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("pt-PT");

Defining this at the begining of your application will make all double parses expect a comma as the decimal delimiter. You can set an appropriate locale so that the decimal and thousands separator fits the strings you are parsing.

Solution 14 - C#

It's difficult without specifying what decimal separator to look for, but if you do, this is what I'm using:

	public static double Parse(string str, char decimalSep)
	{
		string s = GetInvariantParseString(str, decimalSep);
		return double.Parse(s, System.Globalization.CultureInfo.InvariantCulture);
	}

	public static bool TryParse(string str, char decimalSep, out double result)
	{
		// NumberStyles.Float | NumberStyles.AllowThousands got from Reflector
		return double.TryParse(GetInvariantParseString(str, decimalSep), NumberStyles.Float | NumberStyles.AllowThousands, System.Globalization.CultureInfo.InvariantCulture, out result);
	}

	private static string GetInvariantParseString(string str, char decimalSep)
	{
		str = str.Replace(" ", "");

		if (decimalSep != '.')
			str = SwapChar(str, decimalSep, '.');

		return str;
	}
	public static string SwapChar(string value, char from, char to)
	{
		if (value == null)
			throw new ArgumentNullException("value");

		StringBuilder builder = new StringBuilder();

		foreach (var item in value)
		{
			char c = item;
			if (c == from)
				c = to;
			else if (c == to)
				c = from;

			builder.Append(c);
		}
		return builder.ToString();
	}

    private static void ParseTestErr(string p, char p_2)
	{
		double res;
		bool b = TryParse(p, p_2, out res);
		if (b)
			throw new Exception();
	}

	private static void ParseTest(double p, string p_2, char p_3)
	{
		double d = Parse(p_2, p_3);
		if (d != p)
			throw new Exception();
	}

	static void Main(string[] args)
	{
		ParseTest(100100100.100, "100.100.100,100", ',');
		ParseTest(100100100.100, "100,100,100.100", '.');
		ParseTest(100100100100, "100.100.100.100", ',');
		ParseTest(100100100100, "100,100,100,100", '.');
		ParseTestErr("100,100,100,100", ',');
		ParseTestErr("100.100.100.100", '.');
		ParseTest(100100100100, "100 100 100 100.0", '.');
		ParseTest(100100100.100, "100 100 100.100", '.');
		ParseTest(100100100.100, "100 100 100,100", ',');
		ParseTest(100100100100, "100 100 100,100", '.');
		ParseTest(1234567.89, "1.234.567,89", ',');    
		ParseTest(1234567.89, "1 234 567,89", ',');    
		ParseTest(1234567.89, "1 234 567.89",     '.');
		ParseTest(1234567.89, "1,234,567.89",    '.');
		ParseTest(1234567.89, "1234567,89",     ',');
		ParseTest(1234567.89, "1234567.89",  '.');
		ParseTest(123456789, "123456789", '.');
		ParseTest(123456789, "123456789", ',');
		ParseTest(123456789, "123.456.789", ',');
		ParseTest(1234567890, "1.234.567.890", ',');
	}

This should work with any culture. It correctly fails to parse strings that has more than one decimal separator, unlike implementations that replace instead of swap.

Solution 15 - C#

Here is a solution that handles any number string that many include commas and periods. This solution is particular for money amounts so only the tenths and hundredths place are expected. Anything more is treated as a whole number.

First remove anything that is not a number, comma, period, or negative sign.

string stringAmount = Regex.Replace(originalString, @"[^0-9\.\-,]", "");

Then we split up the number into the whole number and decimal number.

string[] decimalParsed = Regex.Split(stringAmount, @"(?:\.|,)(?=\d{2}$)");

(This Regex expression selects a comma or period that is two numbers from the end of the string.)

Now we take the whole number and strip it of any commas and periods.

string wholeAmount = decimalParsed[0].Replace(",", "").Replace(".", "");

if (wholeAmount.IsNullOrEmpty())
		wholeAmount = "0";

Now we handle the decimal part, if any.

string decimalAmount = "00";

if (decimalParsed.Length == 2)
	{
		decimalAmount = decimalParsed[1];
	}

Finally we can put the whole and decimal together and parse the Double.

double amount = $"{wholeAmount}.{decimalAmount}".ToDouble();

This will handle 200,00, 1 000,00 , 1,000 , 1.000,33 , 2,000.000,78 etc.

Solution 16 - C#

System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.CurrentCulture;

string _pos = dblstr.Replace(".",
    ci.NumberFormat.NumberDecimalSeparator).Replace(",",
        ci.NumberFormat.NumberDecimalSeparator);

double _dbl = double.Parse(_pos);

Solution 17 - C#

The below is less efficient, but I use this logic. This is valid only if you have two digits after decimal point.

double val;

if (temp.Text.Split('.').Length > 1)
{
    val = double.Parse(temp.Text.Split('.')[0]);

    if (temp.Text.Split('.')[1].Length == 1)
        val += (0.1 * double.Parse(temp.Text.Split('.')[1]));
    else
        val += (0.01 * double.Parse(temp.Text.Split('.')[1]));
}
else
    val = double.Parse(RR(temp.Text));

Solution 18 - C#

Multiply the number and then divide it by what you multiplied it by before.

For example,

perc = double.Parse("3.555)*1000;
result = perc/1000

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLegateView Question on Stackoverflow
Solution 1 - C#mmxView Answer on Stackoverflow
Solution 2 - C#Pierre-Alain VigeantView Answer on Stackoverflow
Solution 3 - C#Malus JanView Answer on Stackoverflow
Solution 4 - C#BaludaView Answer on Stackoverflow
Solution 5 - C#AndresRohrAtlasInformatikView Answer on Stackoverflow
Solution 6 - C#YakeenView Answer on Stackoverflow
Solution 7 - C#MartinView Answer on Stackoverflow
Solution 8 - C#eduherminioView Answer on Stackoverflow
Solution 9 - C#JanWView Answer on Stackoverflow
Solution 10 - C#SchorschView Answer on Stackoverflow
Solution 11 - C#AlexanderView Answer on Stackoverflow
Solution 12 - C#JacekKView Answer on Stackoverflow
Solution 13 - C#Miguel Mesquita AlfaiateView Answer on Stackoverflow
Solution 14 - C#osexpertView Answer on Stackoverflow
Solution 15 - C#A. FinityView Answer on Stackoverflow
Solution 16 - C#Kartal TurgutView Answer on Stackoverflow
Solution 17 - C#expertatworkView Answer on Stackoverflow
Solution 18 - C#percyView Answer on Stackoverflow