Real Numbers are not Real#
To say that real numbers aren’t real is not just a play on words, but a computational reality. Let’s do a simple experiment: try the following in your favorite number-crunching tool. In Excel:
=IF(1+1E-016 = 1,1,0)
will print 1. In Python:
>>> 1 == 1+1e-16
True
In C, the code
#include<stdio.h>
int main(void)
{
if (1+1e-16 == 1) printf("True\n");
else printf("False\n");
return 0;
}
will print True
. In R:
> 1 == 1+1e-16
[1] TRUE
Note that this behavior is not restricted to small numbers; it also happens with larger numbers. For example:
>>> 1+1e16 == 1e16
True
This shows that the precision of the result depends on the relative scale of the involved numbers.
Although this behavior is typical, there are some exceptions. One is the GNU-bc command line tool:
> bc
1.0 == 1.0+10^(-16)
1
scale=20
1.0 == 1.0+10^(-16)
0
1.0 == 1.0+10^(-21)
1
When we set the scale
parameter to \(20\), the code is able to
recognize that the numbers are different. This just shifts the bar,
though; bc
still fails to recognize the difference between the last
two numbers. Another library that allows for extended, or even
unlimited (up to memory) precision is the GNU Multiple Precision
Arithmetic Library, but its details are beyond
the scope of this document.
The reason for these failures is that computers must store numbers as a sequence of bits, and most common implementations adhere to the IEEE 754 standard. In particular, IEEE-754 sets the standard for double-precision format. This standard is so pervasive that almost all computers have specialized hardware to improve performance for operations on numbers represented as such. One consequence is that mathematical operations on alternative extended number representations tend to be significantly slower than operations on numbers represented following the IEEE 754 standard. Degradation of 10X or even 100X are common.
Due to the performance obtained from hardware support for
double-precision arithmetic, Gurobi relies on this standard (as does
most software). However, this speed comes at a cost: computed results
often differ from what mathematics may dictate. For example, the
associative property (a + (b + c) = (a + b) + c
) is a fundamental
property of arithmetic, but double-precision arithmetic gives (in
Python):
>>> (1+1e-16)+1e-16 == 1 + (1e-16 + 1e-16)
False
Furthermore, many common numbers (e.g. 0.1) cannot be represented exactly.
Consequently, simple questions like whether two numbers are equal, or whether a number is equal zero, or whether a number is integral, can be quite complicated when using floating-point arithmetic.