Type Systems

Inexactness of Type double

While the values of type int are always exact, the values of type double are accurate only to about 15 decimal digits. Those values, however, are hiding some unexpected surprises. As it is usual on today’s computers, type double is not storing decimal digits. Instead, the storage of digits is based on the binary numeral system.

As you may already know, in the decimal number system some values cannot be exactly represented by a finite amount of digits. For example the number with a value 10/3, which is commonly written down as 3.333333…, needs an infinite amount of digits 3 to be written down exactly. So does the number that has a value equal to 10/7, the number π, and many others.

The same issue occurs in the binary numeral system: some numbers cannot be represented by a finite amount of digits. Furthermore, those numbers that need an infinite amount of digits in this system might need only a finite amount of digits in the decimal numeral system.

Let us examine, for example, the number 1/10. The representation of this number in the decimal numeral system is 0.1, obviously requiring 2 decimal digits. However, this number cannot be represented by a finite amount of digits in the binary numeral system. Neither can the number 0.3, for example.

So, when you write a statement like:

double x = 0.1;

then the value assigned to variable x is not exactly equal to 0.1. Even worse, if the next statement begins with:

if(x*3 == 0.3)

then the condition in the if statement is likely to be false because the variable x is not exactly equal to 0.1 and the literal expression 0.3 is not exactly equal to value 0.3. This issue is commonly quite unexpected.

In some cases the result of such computations is sufficiently close to the correct answer to make the condition be true, as a consequence of rounding.

The mentioned problems with accuracy do not affect the values of type int. However, the values of type int are limited to about 9 decimal digits when using most widespread C++ compilers.

Warning! Comparing values of type double by operator == or operator != is dangerous. The result of such comparisons might be unexpected and apparently mathematically incorrect.
You have been warned!

This same issue affects most other common programming languages. The calculations on values of type double are primarily designed to be fast, so they are implemented in the binary numeral system and directly in hardware of modern computers.

If you would like to compare values of type double for equality (or inequality), you need to write a condition like this:

if(x*3 > 0.299999 && x*3 < 0.300001)

The value 0.000001 can be more succinctly written as the literal expression 1e-6, so this condition can also be written as:

if(x*3 > 0.3 - 1e-6 && x*3 < 0.3+1e-6)

There is a better way to write this condition by using functions, explained in a later chapter.

The type float is very similar to type double, but it is accurate only to about 7 decimal digits. The implementation of those two types is almost identical, so the same issues affect the values of type float.

Run the following program with a debugger and pause it at the last line. The debugger should show the discrepancy of variable values in the Locals window.

#include <iostream>
using namespace std; 

int main()  
{      
    double x = 0.1;
    double y = x*3;
    double z = 0.3;
            
    cout << "x: " << x << endl;
    cout << "y: " << y << endl;
    cout << "z: " << z << endl;    
    cout << "Pause the program at this line with debugger" << endl;
}