Reputation: 38861
C++'s std::numeric_limits<float>::digits10
, is described on cppref as such:
The value of
std::numeric_limits<T>::digits10
is the number of base-10 digits that can be represented by the type T without change, that is, any number with this many decimal digits can be converted to a value of type T and back to decimal form, without change due to rounding or overflow.
A similar description exists for the C cousin FLT_DIG.
The value given is:
float FLT_DIG /* 6 for IEEE float */
However, it is shown here on S.O. that all integers up to 16,777,216
(224) are exactly representable in a 32 bit IEEE float type. And if I can count, that number has 8 digits, so the value for digits10
should actually be 7, now shouldn't it?
Rather obviously, I misunderstand something about digits10
here, so what does this actually tell me?
Practical applicability:
I was asked if we could store all numbers from 0.00
- 86,400.00
exactly in an IEEE 32 bit float.
Now, I'm very confident that we could store all numbers from 0
- 8,640,000
in an IEEE 32 bit float, but does this hold for the same "integer" range shifted by 2 digits to the left?
Upvotes: 4
Views: 311
Reputation: 154315
Sometimes it is easy enough to look for counter examples.
#include <stdio.h>
#include <string.h>
int main(void) {
int p6 = 1e6;
int p7 = 1e7;
for (int expo = 0; expo < 29; expo++) {
for (int frac = p6; frac < p7; frac++) {
char s[30];
sprintf(s, "%d.%06de%+03d", frac / p6, frac % p6, expo);
float f = atof(s);
char t[30];
sprintf(t, "%.6e", f);
if (strcmp(s, t)) {
printf("<%s> %.10e <%s>\n", s, f, t);
break;
}
}
}
puts("Done");
}
Output
<8.589973e+09> 8.5899735040e+09 <8.589974e+09>
<8.796103e+12> 8.7961035080e+12 <8.796104e+12>
<9.007203e+15> 9.0072024760e+15 <9.007202e+15>
<9.223377e+18> 9.2233775344e+18 <9.223378e+18>
<9.444738e+21> 9.4447374693e+21 <9.444737e+21>
<9.671414e+24> 9.6714134744e+24 <9.671413e+24>
<9.903522e+27> 9.9035214949e+27 <9.903521e+27>
<1.000000e+28> 9.9999994421e+27 <9.999999e+27> This is an interesting one
Upvotes: 2
Reputation: 38861
As the other answer and comment establishes, digits10
covers all "exponent ranges", that is it has to hold for 1234567
as well as for 1.234567
and 12345670000
-- and this only holds for 6 digits!
Counter example for 7 digits:
8.589,973 e9
vs. 8.589,974 e9
(from cppref example)Upvotes: 3
Reputation: 154315
Another point of view:
Consider between each pair of powers-of-2, a float
like IEEE binary encodes 223 values distributed linearly.
Example: Between 20 and 21 or 1.0 and 2.0,
The difference between float
values is 1.0/223 or 10.192e-06.
Written in text form "1.dddddd", a 7 digit number, the numbers have a difference of 1.000e-06.
So for every step of a decimal text number, there are about 10.2 float
.
No problems encoding these 7 digit numbers.
In this range, no problems encoding 8 digits either.
Example: Between 223 and 224 or 8,388,608.0 and 16,777,216.0.
The difference between float
values is 223/223 or 1.0.
The numbers near the low end written in text form "8or9.dddddd*106", a 7 significant digit number, have a difference of 1.0.
No problems encoding these 7 digit numbers.
Example: Between 233 and 234 or 8,589,934,592.0 and 17,179,869,184.0,
The difference between float
values is 233/223 or 1,024.0.
Numbers near the low end written in text form "8or9.dddddd*109", a 7 significant digit number, have a difference of 1,000.0.
Now we have a problem. From 8,589,934,592.0, then next 1024 numbers in text form only have 1000 different float
encoding.
7 digits in the form d.dddddd * 10expo is too many combinations to uniquely encode using float
.
Upvotes: 1
Reputation: 234785
(Restricting this answer to IEEE754 float
).
8.589973e9
and 8.589974e9
both map to 8589973504
. That's a counter-example for an assertion that the 7th significant figure is preserved.
Since no such counter-example exists on the 6th significant figure, std::numeric_limits<float>::digits10
and FLT_DIG
are 6.
Indeed integers can be represented exactly up to the 24th power of 2. (16,777,216
and 16,777,217
both map to 16,777,216
). That's because a float
has a 24 bit significand.
Upvotes: 10