Parsing floating point numbers

Parsing floating point numbers

One of my StackOverflow answers is about converting string to double. I answered the question long time ago, but it still gets me some upvotes. It looks like this topic requires some more attention. In this post I’m going to show you about possible pitfalls of parsing float/double numbers. You’re also going to learn how to safely convert string to float and string to double.

This post is language agnostic, meaning that the concepts do not depend on specific language. However I’m going to include code snippets in C#.

Culture awareness

Let’s start off with culture. This is an important topic, which at times may cause trouble. Simply said, different countries (“languages”/cultures/locale) have different ways of formatting numbers.

First key point here is that you may want to display those numbers depending on user culture. This is the “easier” part of culture aware application, because it only relates to how we print and format numbers.

Second key point is the input. When we ask users for input data, we usually expect it to be in a format we can process. Since numbers can be formatted differently we cannot really be sure what is the value of sent data, unless we know what is the user culture/locale or we enforce same data format for all users. A common example of this is date eg. 12/01, this can be interpreted as 12th january or 1st december depending on where the user is from.

Decimal separator

As an example let us look at the following use case. Imagine we’re having a bank transfer form and we need to input how much money we want to send. User enters 5 and, you guessed it, application accepts a $5 transfer transaction. Let’s try something more difficult. What would happen when user enters 5,55? It depends. Some cultures use comma as a thousands separator (which basically helps to group numbers), some would accept space and some don’t use a thousands separator at all. In other cultures comma is an accepted as a decimal separator whereas some use dot.

In the example above our app could theoretically interpret 5,55 as 555 (eg. basically ignoring comma as a thousands separator). Fun begins when our software is build for international users. You either have to force one format for all users or you have to make sure about culture on each step. This is important in both client-side and server-side processing code. In essence it’s a matter of interpretation.

Parsing numbers in C#

There area a couple of ways we can parse numbers in C#. First one would be a Convert class, which as the name implies, allows us to convert value of one type to other. Second way is to use float.Parse (when you are sure about input format) or float.TryParse (if you’re not sure about the input format). Similary int and double also has Parse/TryParse methods.

using System;

var value = Convert.ToDouble("1.23");
// if your system culture accepts DOT as a decimal separator it will pass
// if your system culture accepts COMMA as a decimal separator it will throw StringFormatException
// when you're confident about input string format use Parse
var value = double.Parse("3.14");

// not sure wheter string is a valid double? use TryParse
var isValidDouble = double.TryParse("3.14", out double result);

Culture aware parsing in C#

Since different cultures use different formats we can either use an InvariantCulture, specific (existing) culture or custom (new) culture.

InvariantCulture basically is an specific predefined culture, which cannot be changed. It means that it always stays the same regardless of user or currently selected culture or .NET version. It’s important because it stays the same, always providing the same format. We can use it in serialization (e.g. JSON) to make sure that our numbers always use dot as decimal separator. Since it’s artificial it does not relate to any specific locale.

// InvariantCulture uses dot as a decimal separator
using System.Globalization;

double.Parse("3.14", NumberStyles.Any, CultureInfo.InvariantCulture, out double result);

If you want to use specific culture (like en-us) then you can do this: