This guide will explain the programming issues you will encounter when performing arithmetic with floating point numbers, and how to avoid them.
The reason why these issues occur will be summarised and solutions will be provided.
Floating point precision issues are common hurdle for beginner programmers – sometimes the result of what should be a simple mathematical operation comes out wrong, and little reason is given. Floating point precision issues are usually the cause, so here’s what they are and why this problem occurs.
What is a Floating Point Number?
A floating point number is a number with a decimal point, where there can be any number of digits either side of the decimal point.
Hence floating point – the decimal point isn’t in a fixed position, it ‘floats’ to the left or right depending on how many digits are either side of it.
A floating point number is a common data type in programming languages. Where the integer datatype stores a whole number, floats or floating point numbers store numbers with a varying number of decimal points.
Integers can be stored as a precise value. Decimal numbers can be stored precisely also – as a pair of integers with the decimal at a fixed point, as number of digits to the right of it is known.
The inexact nature of the length of floating point numbers, however, presents an issue for computers. The variable precision makes storing floating point numbers a difficult task for a computer.
Floating Point Arithmetic – Or – Why The Sum of Your Floating Point Numbers is Wrong
So, computers suck at handling floating point numbers. What does that mean for you as a programmer?
Quite a bit. Any math/arithmetic you do with floating point numbers may produce an inaccurate answer if you aren’t careful.
Open up a JavaScript console and enter the following:
0.1 + 0.7
This is a simple statement which adds two floating point numbers, the answer should be:
0.8
…Right? But instead you will see:
0.7999999999999999
Your computer has failed to accurately calculate the result of 0.1 + 0.7 due to the fact that it can’t accurately represent floating point numbers.
This doesn’t mean that the value 0.8 can’t be stored! It just means that the result of 0.1 + 0.7 cannot be calculated accurately.
Why Does this Happen?
In short, rounding errors. Computers store and process numerical values as binary numbers – numbers expressed in base 2 (represented by a series of digits containing 0 or 1). Humans usually use decimal numbers which are expressed in base 10 (represented by a series of digits containing 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9)
Binary numbers are more efficient for present-day computers to process, but they cannot accurately represent certain values. 1/3 cannot be accurately represented in base 2 so it must be rounded to 0.33. This is fine most of the time, and the trade-off for efficiency is worth it for most computing tasks.
However, in some applications these tiny differences add up and must be dealt with.
Why is this a Big Deal?
It’s a tiny difference, but it could have big implications so it’s worth making sure that you get into the habit of noting where floating point arithmetic issues are likely to occur, and ensuring they are corrected for.
The most basic place this causes issues is when comparing numbers. If you are comparing the result of two arithmetic operations for equality, or to see which is larger, you may get unexpected results if one of those numbers is inaccurate due to a floating point error.
Floating point errors can also cause systematic issues for a program or a business which relies on it.
Consider that you are building an online shop which generates yearly financial reports. A tiny 0.00000000000000001 discrepency in each sale won’t matter much at the time the sale is made – the receipt will no doubt show a rounded value for display, and the value will be stored and that tiny error will forever be rounded when displayed on a report or screen and be of little consequence.
However, if you start making millions of sales a day (lucky you), all of a sudden your bank balance will start to drift away from the value on your report – those differences are adding up and starting to cause problems.
This could result in having to correct huge amounts of historical data, re-filing tax reports, getting yelled at by the boss – all which could have been avoided if you’d corrected for this issue when first developing your online store.
Mitigating/Correcting Floating Point Maths Issues
There are a number of ways to correct for floating point math issues, and there are helper libraries available which you can integrate into your projects to help get around these problems.
Common Programming Techniques
Here are some techniques commonly used by developers regardless of what programming environment they are using.
Rounding Numbers
The most common technique for correcting for floating point precision issues is to simply round the result of all mathematical operations to the number of decimal places required for the current situation.
For example, if you are working with numbers representing US dollars paid in cash, (dollars and cents), you know that you will need two digits after the decimal place, and never any more.
So long as the result of any arithmetic/mathematical operations is rounded to two decimal places, it will be accurate for the purposes of your application:
(0.1 + 0.7, 2).toFixed(2);
Which will return:
0.80
Above, the problematic floating point math shown previously is rounded using the Javascript toFixed() method, and is accurate for the intended purpose.
Working with Integers Instead of Decimals
Integer math does not suffer the accuracy issues of floating point math. If you know how many decimal places are required for the purpose your numbers will be used for, you can store and perform calculations with the number as an integer and divide it by a power of 10 later to get the decimal value.
Taking again the US dollar scenario above, if we’re working to a known number of decimal places (2), we can store all values as integers and divide by 10 later:
(1 + 7) / 10
Which will output:
0.8
Above, 0.1 and 0.7 were stored as the integer values 1 and 7. Math performed on these integers is accurate. They are then converted to their correct order of magnitude by dividing by 10.
Any code writing to the data store would need to ensure the values were multiplied by 10 to get an integer value to store, and any code reading from the data store would need to divide by 10 after any calculations when displaying the value to the user.
Ensure the Right Value Type is Stored in your Database
Regardless of your database system, make sure you are using the right column type to store your data. Most database solutions support decimal data types with a fixed decimal point for accuracy.
Integers can also be stored instead if you want to convert to/from as in the example above.
Some developers will even store some numerical values as strings so that they cannot be affected by floating point rounding issues, parsing the string to the correct data type manually in their code when they need to perform arithmetic with it.
PHP
The PHP programming language documentation contains a whole section on floating point math, with user submitted tips. It’s a great resource which is kept updated so rather than parrot the contents, I’ll just link to it right here.
JavaScript / TypeScript
Javascript is probably the worst offender when it comes to floating point maths as it is so loosely typed. If a variable is initalised as a floating point number, if it is used in an expression with a number of another type, the result may wind up being a floating point number and start accumulating precision errors. The best way to avoid this is to use typing if it is available through Typescript as well using math libraries to assist with your arithmetic.
Math Libraries
Math.js is a library for doing math in Javascript (surprise). It contains functions for all arithmetic operations, and while it does not correct innacurate results generated by floating point math, it DOES allow you to perform your arithmetic using fractions or using BigNumbers – a custom data-type which more accurately stores both integer and float values of arbitrary lengths.
Use Type-Safe ORM
An object relation manager (ORM) maps the data from your Javascript application onto a database and performs all of the conversions required to accurately store your data in your database columns.
Using a type-safe ORM like Prisma will mean your data is stored in the correct database column. Information stored in custom object classes will also have their data-types enforced, so that you are less likely to accidentally store a number in the wrong format.
Python
The Python documentation contains a page outlining how Python is affected by floating point math issues.
Python 3 contains a decimal class specifically for storing decimal numbers to help avoid floating point issues.
For Further Information…
I’ve stayed away from the real nitty-gritty of how your computer processes floating point numbers and performs arithmetic information. Now that you’re familiar with the floating point arithmetic issue in programming and know how to avoid it, if you really want to get into detail, this site is a great starting point.