How to Convert String to Double in C#

The safest way to convert string to double in C# is double.TryParse with an explicit culture. It returns false on bad text instead of crashing:

using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
string input = "3.459";
if (double.TryParse(input, CultureInfo.InvariantCulture, out double price))
    Console.WriteLine(price * 2);   // 6.918

Every snippet below ran on .NET 10 against a 2026 fuel log, including blank input, European commas and numbers too big for a double.

double.Parse: Convert a String to Double When You Trust the Text

double.Parse returns the number or throws. Use it for data you control, like a config file or your own CSV export.

My running sample is a 2026 fuel log from Desert Line Freight, a made-up trucking company in Phoenix. Here are five cells from it:

using System.Globalization;

var inv = CultureInfo.InvariantCulture;
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
// Desert Line Freight, Phoenix: values from a 2026 fuel log
string[] cells = { "3.459", "-12.5", " 72.8 ", "1,234.56", "1.5E3" };

foreach (string cell in cells)
{
    double value = double.Parse(cell, inv);
    Console.WriteLine($"{"\"" + cell + "\"",-12} -> {value}");
}

Output:

"3.459"      -> 3.459
"-12.5"      -> -12.5
" 72.8 "     -> 72.8
"1,234.56"   -> 1234.56
"1.5E3"      -> 1500
VS Code running ParseDouble.cs: C# double.Parse with CultureInfo.InvariantCulture converts 3.459, -12.5, 1,234.56 and 1.5E3 to double values
double.Parse accepts signs, spaces, thousands commas and exponents

All five parse with CultureInfo.InvariantCulture. The default style is NumberStyles.Float | AllowThousands, so a sign, padding spaces, a thousands comma and 1.5E3 (1500) are all fine.

double.TryParse: The Recommended Way for User Input

For anything a person types, double.TryParse is the better pick. It returns a bool and puts the number in an out variable, so bad input never throws. Same fuel log, now trip miles a driver typed into a form:

using System.Globalization;

var inv = CultureInfo.InvariantCulture;
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
string?[] typed = { "418.6", "abc", "", null, "12.5.3", "12,5" };

foreach (string? miles in typed)
{
    string shown = miles is null ? "null" : $"\"{miles}\"";
    if (double.TryParse(miles, inv, out double value))
        Console.WriteLine($"{shown,-9} -> {value}");
    else
        Console.WriteLine($"{shown,-9} -> invalid, value = {value}");
}

Output:

"418.6"   -> 418.6
"abc"     -> invalid, value = 0
""        -> invalid, value = 0
null      -> invalid, value = 0
"12.5.3"  -> invalid, value = 0
"12,5"    -> 125
VS Code running TryParseMiles.cs: C# double.TryParse returns false for abc, an empty string, null and 12.5.3 and sets the value to 0
TryParse returns false and 0 for bad input, with no exception

“abc”, the empty string, null and “12.5.3” all come back invalid with a value of 0. That’s cheaper and cleaner than a try-catch block in C# around Parse.

Important: “12,5” became 125. Under the invariant culture the comma is a thousands separator, and .NET doesn’t check where it sits. If a driver in Europe could type that, see the culture section.

It’s the same pattern you’d use for whole numbers; converting a string to int without int.Parse shows the integer side.

Convert.ToDouble: How Is It Different from double.Parse?

Convert.ToDouble(string) calls double.Parse under the hood, with one difference: a null string returns 0 instead of throwing.

using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
string? missing = null;

Console.WriteLine(Convert.ToDouble("3.459"));
Console.WriteLine(Convert.ToDouble(missing));   // 0, no exception

try { double.Parse(missing!); }
catch (ArgumentNullException ex) { Console.WriteLine(ex.GetType().Name); }

try { Convert.ToDouble(""); }
catch (FormatException ex) { Console.WriteLine(ex.GetType().Name); }

Output:

3.459
0
ArgumentNullException
FormatException

Convert.ToDouble(null) printed 0, while double.Parse on the same null threw ArgumentNullException. An empty string still throws FormatException with both.

Silent 0 can hide a missing value, so I only use it when 0 really is the right default. Converting null to int in C# covers the same trade-off for integers.

Why Culture Matters When You Parse a Double: “1,5” in en-US vs de-DE

Calls without a culture use the machine’s CultureInfo.CurrentCulture. In Germany, France and most of Europe, the comma is the decimal separator. Here are the same two strings under three cultures:

using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
var cultures = new[] { CultureInfo.InvariantCulture,
    CultureInfo.GetCultureInfo("en-US"), CultureInfo.GetCultureInfo("de-DE") };

foreach (var culture in cultures)
{
    double a = double.Parse("1,5", culture);
    double b = double.Parse("1.5", culture);
    string name = culture.Name == "" ? "Invariant" : culture.Name;
    Console.WriteLine($"{name,-10} \"1,5\" -> {a,-4} \"1.5\" -> {b}");
}

Output:

Invariant  "1,5" -> 15   "1.5" -> 1.5
en-US      "1,5" -> 15   "1.5" -> 1.5
de-DE      "1,5" -> 1.5  "1.5" -> 15
VS Code running CultureDouble.cs: C# double.Parse reads 1,5 as 15 in en-US and Invariant, but as 1.5 in de-DE, and 1.5 as 15 in de-DE
Same text, different culture, values 10x apart and no exception

Invariant and en-US read “1,5” as 15; de-DE reads it as 1.5. Flip it, and de-DE turns “1.5” into 15. Nothing throws, so the bug ships.

My rule: CultureInfo.InvariantCulture for files, APIs and JSON you control; the user’s culture for what people type; never the default. My own dev PC isn’t set to en-US, which is exactly how this bites.

Thousands Separators and Currency Strings (NumberStyles)

Spreadsheet exports often keep a $ sign. The default style refuses it, so pass NumberStyles.Currency:

using System.Globalization;

var us = CultureInfo.GetCultureInfo("en-US");
CultureInfo.CurrentCulture = us;

double total = double.Parse("$1,234.56", NumberStyles.Currency, us);
Console.WriteLine(total);

bool ok = double.TryParse("1,234.56", NumberStyles.Float, us, out _);
Console.WriteLine($"Float style with a comma: {ok}");

bool ok2 = double.TryParse("$1,234.56", us, out _);
Console.WriteLine($"Default style with $: {ok2}");

Output:

1234.56
Float style with a comma: False
Default style with $: False

“$1,234.56” becomes 1234.56 with Currency. NumberStyles.Float rejects the thousands comma, and the default style rejects the $ sign.

The same flags work on decimal; converting a number with a comma to decimal has the full NumberStyles table and European “1.234,56” amounts.

double.Parse Errors: FormatException, OverflowException and Infinity

double.Parse throws FormatException for text that isn’t a number. Overflow is different on modern .NET:

using System.Globalization;

var inv = CultureInfo.InvariantCulture;
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");

try { double.Parse("12.5 gal", inv); }
catch (FormatException) { Console.WriteLine("FormatException: 12.5 gal"); }

double huge = double.Parse("1e400", inv);   // no OverflowException
Console.WriteLine($"1e400 -> infinity? {double.IsPositiveInfinity(huge)}");

double nan = double.Parse("NaN", inv);
Console.WriteLine($"NaN -> {double.IsNaN(nan)}");
Console.WriteLine($"Safe to use? {double.IsFinite(huge)}");

Output:

FormatException: 12.5 gal
1e400 -> infinity? True
NaN -> True
Safe to use? False
VS Code running ParseErrors.cs: C# double.Parse throws FormatException for 12.5 gal, returns infinity for 1e400 and NaN for NaN on .NET 10
On .NET 10, 1e400 parses to infinity instead of throwing OverflowException

“12.5 gal” throws, as expected. But “1e400” didn’t throw OverflowException; it returned positive infinity. Since .NET Core 3.0, only .NET Framework throws there.

“NaN” parses too. So after a successful parse, check double.IsFinite before you divide or save the value (the division by zero guide shows why infinity sneaks through).

double vs decimal for Money

A double stores binary fractions, so some decimal values can’t be exact. Two prices from the fuel log show it:

using System.Globalization;

var inv = CultureInfo.InvariantCulture;
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");

double d = double.Parse("0.1", inv) + double.Parse("0.2", inv);
decimal m = decimal.Parse("0.1", inv) + decimal.Parse("0.2", inv);

Console.WriteLine(d);   // double
Console.WriteLine(m);   // decimal

Output:

0.30000000000000004
0.3

The double sum prints 0.30000000000000004; the decimal sum prints 0.3. For dollars, parse to decimal instead. The C# data types guide compares the two.

If you only need to show a double with two decimals, that’s a formatting job; see how to show a double with 2 decimal places.

Parse a Span or UTF-8 Bytes Without Extra Strings

double.Parse also takes a ReadOnlySpan<char> and, since .NET 8, UTF-8 bytes. That lets you parse a slice of a CSV line without Substring:

using System.Globalization;
using System.Text;

var inv = CultureInfo.InvariantCulture;
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
string line = "Phoenix,AZ,418.6";

ReadOnlySpan<char> miles = line.AsSpan(line.LastIndexOf(',') + 1);
Console.WriteLine(double.Parse(miles, inv));    // no Substring copy

byte[] utf8 = Encoding.UTF8.GetBytes("3.459");
Console.WriteLine(double.Parse(utf8, inv));     // UTF-8 bytes

Output:

418.6
3.459

Both lines print the real values, 418.6 and 3.459. It’s worth it in hot loops over large files; for one form field, the string overload is fine.

Once the text is a number, these are good next reads:

Frequently Asked Questions

How do I convert a string to double in C#?

Use double.TryParse(text, CultureInfo.InvariantCulture, out double value) for input you don’t control, and double.Parse(text, CultureInfo.InvariantCulture) when you’re sure the text is a valid number.

What’s the difference between double.Parse and Convert.ToDouble?

Convert.ToDouble calls double.Parse, but returns 0 for a null string. double.Parse(null) throws ArgumentNullException. Both throw FormatException for text like “abc” or an empty string.

Why does double.Parse return 15 for “1,5”?

Under en-US or the invariant culture, the comma is a thousands separator, so “1,5” reads as 15. Pass a culture that uses a comma decimal, such as CultureInfo.GetCultureInfo("de-DE"), to get 1.5.

Does double.Parse throw OverflowException in .NET 10?

No. On .NET Core 3.0 and later, a value like “1e400” returns positive infinity. Only .NET Framework throws OverflowException. Check double.IsFinite after parsing.

How do I convert a string with a $ sign to double?

Use double.Parse("$1,234.56", NumberStyles.Currency, CultureInfo.GetCultureInfo("en-US")), which returns 1234.56. For money math, parse to decimal instead.

Where is double.Parse documented?

The Double.Parse method on Microsoft Learn lists every overload, the default NumberStyles and the .NET Core 3.0 overflow change.