I am trying to write a program that outputs the number of the digits in the decimal portion of a given number (0.128
).
I made the following program:
#include <stdio.h>
#include <math.h>
int main(){
float result = 0;
int count = 0;
int exp = 0;
for(exp = 0; int(1 result) % 10 != 0; exp )
{
result = 0.128 * pow(10, exp);
count ;
}
printf("%d \n", count);
printf("%f \n", result);
return 0;
}
What I had in mind was that exp
keeps being incremented until int(1 result) % 10
outputs 0
. So for example when result = 0.128 * pow(10,4) = 1280
, result mod 10 (int(1 result) % 10
) will output 0
and the loop will stop.
I know that on a bigger scale this method is still inefficient since if result
was a given input like 1.1208
the program would basically stop at one digit short of the desired value; however, I am trying to first find out the reason why I'm facing the current issue.
My Issue: The loop won't just stop at 1280
; it keeps looping until its value reaches 128000000.000000
.
Here is the output when I run the program:
10
128000000.000000
Apologies if my description is vague, any given help is very much appreciated.
CodePudding user response:
I am trying to write a program that outputs the number of the digits in the decimal portion of a given number (
0.128
).
This task is impossible, because the goal is not meaningful.
If I write
float f = 0.128;
printf("%f\n", f);
I see
0.128000
and I might conclude that 0.128 has three digits. (Never mind about the three 0
's.)
But if I then write
printf("%.15f\n", f);
I see
0.128000006079674
Wait a minute! What's going on? Now how many digits does it have?
It's customary to say that floating-point numbers are "not accurate" and that they tend to suffer from "roundoff error". But in fact, floating-point numbers are, in their own way, perfectly accurate — it's just that they're accurate in base two, not the base 10 we're used to thinking about.
The surprising fact is that the number 0.128 does not exist as a finite binary fraction. This is similar to the way that the number 1/3 does not exist as a finite decimal fraction. You can approximate it as 0.333 or 0.3333333333 or 0.33333333333333333333, but without an infinite number of 3's it's only an approximation. Similarly, you can approximate 1/10 in binary as 0b0.00011
or 0b0.000110011
or 0b0.000110011001100110011001100110011
, but without an infinite number of 0011
's it, too, is only an approximation.
And it's the same with most decimal fractions you can think of — most of them don't have exact binary representations. So when I said
float f = 0.128;
what I actually got in f
was the binary number 0b0.00100000110001001001101111
, which in decimal is exactly 0.12800000607967376708984375.
Once a number has been stored as a float
(or a double
, for that matter) it is what it is: there is no way to rediscover that it was initially initialized from a "nice, round" decimal fraction like 0.128. And if you try to "count the number of decimal digits", and if you do a good job, you're going to get an answer of 26 (that is, the digits "12800000607967376708984375"), not 3.
CodePudding user response:
float
vs. code
A float
cannot encode 0.128 exactly as it is not a dyadic rational.
Instead, it takes on a nearby value: 0.12800000607967376708984375. 26 digits.
Rounding errors
OP's approach incurs rounding errors in result = 0.128 * pow(10, exp);
.
Extended math needed
The goal is difficult. Example: FLT_TRUE_MIN
takes about 149 digits.
We could use double
or long double
to get us somewhat there.
Simply multiply the fraction by 10.0 in each step.
d *= 10.0;
still incurs rounding errors, but less so than OP's approach.
#include <stdio.h>
#include <math.h> int main(){
int count = 0;
float f = 0.128f;
double d = f - trunc(f);
printf("%.30f\n", d);
while (d) {
d *= 10.0;
double ipart = trunc(d);
printf("%.0f", ipart);
d -= ipart;
count ;
}
printf("\n");
printf("%d \n", count);
return 0;
}
Output
0.128000006079673767089843750000
12800000607967376708984375
26