Home > OS >  Determining the number of decimal digits in a floating number
Determining the number of decimal digits in a floating number

Time:10-09

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 
  • Related