A common assumption is that 1 / x * x == 1. What is the least positive integer that breaks this on common IEEE 754-compliant hardware?
When the assumption of a multiplicative inverse fails, poorly-written rational arithmetic ceases to work. Because many languages including C and C++ by default convert floating-point numbers to integers using round-to-zero, even a small error can cause an integral result to be off by one.
A quick test program produces various results.
#include <iostream>
int main () {
{
double n;
for ( n = 2; 1 / n * n == 1; ++ n ) ;
std::cout << n << " (" << 1 - 1/n*n << ")\n";
for ( ; (int) ( 1 / n * n ) == 1; ++ n ) ;
std::cout << n << " (" << 1 - 1/n*n << ")\n";
}
{
float n;
for ( n = 2; 1 / n * n == 1; ++ n ) ;
std::cout << n << " (" << 1 - 1/n*n << ")\n";
for ( ; (int) ( 1 / n * n ) == 1; ++ n ) ;
std::cout << n << " (" << 1 - 1/n*n << ")\n";
}
}
On ideone.com using GCC 4.3.4 the results are
41 (5.42101e-20)
45 (5.42101e-20)
41 (5.42101e-20)
45 (5.42101e-20)
Using GCC 4.5.1 produces the same results but the error margins are reported to be exactly zero.
On my machine (GCC 4.7.2 or Clang 4.1), the results are
49 (1.11022e-16)
49 (1.11022e-16)
41 (5.96046e-08)
41 (5.96046e-08)
This is regardless of the --fast-math option. Using -mfpmath=387 surprisingly produces
41 (5.42101e-20)
41 (5.42101e-20)
41 (5.42101e-20)
41 (5.42101e-20)
The value 5×10-20 seems to imply epsilon corresponding to a 64-bit mantissa, i.e. internal calculations using Intel 80-bit extended precision.
This seems to be highly dependent on FPU hardware. Is there a reliable value that’s good for testing?
Note: I don’t care what language standards or compilers guarantee about floating point number systems, although I don’t think there are many meaningful guarantees in any common programming system. I’m wondering about the interaction between the numbers and real-world computers.
In double precision:
1/41 = 0x1.8f9c18f9c18fap-6, and 41*0x1.8f9c18f9c18fap-6 = 0x1.000000000000028, which rounds to 1.
1/45 = 0x1.6c16c16c16c17p-6, and 45*0x1.6c16c16c16c17p-6 = 0x1.00000000000002c, which rounds to 1.
However,
1/49 = 0x1.4e5e0a72f0539p-6, and 49*0x1.4e5e0a72f0539p-6 = 0x0.fffffffffffffa4, which rounds to 0x0.fffffffffffff8 = 0x1.fffffffffffff0p-1
49 does have a reciprocal, though! It’s 0x1.4e5e0a72f053ap-6.
More generally, if f is a floating-point number in [1, 2), then f has a reciprocal. Under usual round-to-even arithmetic, a number will round to 1 if it lies in [1 – 2-54, 1 + 2-53].
Notice that the closest double, say d, to 1/f is less than 2-54 away from 1/f. If d > 1/f, then we’re golden; 1 < f*d < f*(1/f+2-54) <= 1 + 2-54 * f < 1 + 2-53, so f*d rounds to 1. If d < 1/f, then f*d might round to 1 – 2-53. If it does, then f*d lies in [1 – 2-53, 1 – 2-54). If you take e = 2-53 + d, then e*f > 1 and e*f = d*f + 2-53*f < 1 – 2-53 + 2-52 = 1 + 2-53, which again rounds to 1.
EDIT: The reasoning above is wrong since the stride between two consecutive doubles is off by a factor of two. An example of a double that does not have a reciprocal is 0x1.ffffffbfffffe. 0x1.0000002000001p-1 is too small but 0x1.0000002000002p-1 is too large. The smallest example of an integer that doesn’t have a reciprocal is 237. 1/237 is roughly 0x1.1485f0e0acd3B68c6Bp-8, which rounds to 0x1.1485f0e0acd58p-8. This number is too small, while the next double after it is too large.