Why floats are bad?

We have the habit of using float to represent any number which has a decimal point in it. I used to do it since college days and I have seen many experienced developers do it  which inadvertently sometimes leads to unexpected bugs in our software. While most of the times one can get away with using floats for representing decimal point numbers the issue arises when the quantity we are representing is a monetary value. When it comes to money inaccuracy in arithmetic operations may lead to huge losses. Even a single digit inaccuracy can lead to mistakes of very high magnitude. But why are floats or doubles so bad at representing decimal values and especially when it comes to money?

 fig. An example of floating point inaccuracy

The real reason behind this inaccuracy is  the way in which these data types represent the base 10 multiples which we use for money. It is not possible to encode most decimal fractions in binary without infinite repetitions. Numbers like 0.1 or 0.3 cannot be written accurately in binary. Think, for example, we can write 0.1 perfectly as it is nothing but one tenth, because our number system is based on 10. But in binary (which is what floats use) 0.1 becomes a never ending fraction, similar to what 1/3 becomes in base-10 numbering system. Apart from the accuracy issues, floating point numbers also struggle with rounding errors, overflow or underflow errors. And these errors will just keep on accumulating when we have a group of arithmetic operations taking place one after other.

  

fig. An example showcasing accuracy of Decimal 

Decimals (Python's Decimal, C#'s decimal, Java's BigDecimal) the data type which is supported by most modern programming languages as well as software tools such as modern DBMS do not use binary fractions. Decimals are more accurate than floats or doubles and are a good candidate for storing or representing decimal values in monetary domains such as banking and finance. Decimals are more predictable and precise than floats or doubles. Their precision can also be controlled by us while with floats our hardware (along with floating-point format and language runtime) is the one in charge. Decimals obey the human math rules and are loved by accountants. So should we use Decimals everywhere?

Well, the answer is not so straight forward as we would think. While Decimals are more suitable for Monetary operations, they do come with a cost. Decimal operations are slower as compared to float or double because Decimals are not native types in most languages. These are derived programming constructs or abstraction provided to us (the developer) for easier handling of decimal values. While this does not make any difference in your simple calculator console application, at a larger scale this might matter. So, instead we should use a combination of float/double, string and decimals in the following order:

  • Finance: Never use floats or doubles. Stick to integers and decimals for money with predefined accuracy (usually up to 2 decimal places)

  • Data science and research: Use specialized libraries that are available along with data types included in them. Floats or doubles can be used as in these use cases the performance of the application matters a lot more than arithmetic accuracy.

  • Other applications: Here we can usually get away with using floats or doubles but we must ensure that if there is any part of our application that deals with money or any such significant numeric quantity then we must switch to decimals. 

In REST APIs, it is suitable to use string to represent decimal values present in the request payload or the response body (in JSON format). This will ensure that libraries don't auto type cast such values to float or double and gives this control in the hands of the developer. Hence, it is ideal to use string during API calls to represent numbers in request or response bodies. 

So we can conclude that the order of accuracy of data types for representing decimal values are:

  1. decimals
  2. strings (which should later be type cast into relevant numeric type such as decimals
  3. floats or doubles 


Comments