It is said that any decimal number expressed with 15 digits of precision within the range covered by double-precision floats can be uniquely identified with a particular float. But, that if we include more digits of precision (i.e. 16), we may see that two or more decimal numbers correspond to the same float. See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more details.
Could someone please provide me with an example of two number with 16 decimal digits precision that map to the same double-precision float?
Let us try large 16 decimal digit integers of the form 9 xxx xxx xxx xxx xxx
:
#include <stdio.h>
int main() {
//unsigned long long x0 = 9000000000000000;
unsigned long long x0 = 1uLL << 53; // 9,007,199,254,740,992
unsigned long long x1 = 9999999999999999;
unsigned long long same = 0;
for (unsigned long long x = x0; x <= x1; x++) {
double f0 = (double) x;
double f1 = (double) (x + 1);
if (f0 == f1) {
if (same++ == 0) {
printf("%llu (0x%llX) %llu (0x%llX) both value the value of %f\n", //
x, x, x + 1, x + 1, f0);
break;
}
}
}
printf("Repeats = %llu\n", same);
}
We rapidly find
9007199254740992 (0x20000000000000) 9007199254740993 (0x20000000000001) both value the value of 9007199254740992.000000
Repeats = 1
9007199254740993 (0x20000000000001)
is a 54-binary digit value. Common double
can only exactly encode up to 53-significant binary digit values.
Another way to look at it pigeon hole principle.
Given common double
encoding of 52 binary digits per each power-of-2. Between [0.5 ... 1.0), there are 252 or
4,503,599,627,370,496 different double
values. Yet in that range there are 5,000,000,000,000,000 different 16 digit decimal values of the form 0.add ddd ddd ddd ddd d
(a
= [5...9], d
= [0-9]), so there is not enough distinct double
values to map uniquely to all the 16 decimal digit values of that range. Some decimal values will map to the same double
.
#include <string.h>
#include <stdio.h>
int main() {
// 1234567890123456
long long x1 = 9999999999999999; // 0x23 86F2 6FC0 FFFF
char prior[40] = "0.9999999999999999";
for (long long x = x1; --x > 0; ) {
char buf[40];
sprintf(buf, "0.%16lld", x);
double value = atof(buf);
if (value == atof(prior)) {
printf("%s %s both have value %.20g\n", prior, buf, value);
break;
}
strcpy(prior, buf);
}
printf("Done\n");
}
0.9999999999999995 0.9999999999999994 both have value 0.99999999999999944489
Done