4

I'm trying to count the number of digits in fractional part of double, but something is going wrong and I getting infinite loop:

double pi = 3.141592;
int counter = 0;
for (; pi != int(pi); ++counter)
    pi *= 10;
cout << counter << endl;

I'v just read about this problem, however I can't find a good solution. Is there really no better way than convert a number to string and count chars? I suppose there's more correct method.

hant0508
  • 528
  • 1
  • 9
  • 19

4 Answers4

6

Floating point numbers in computer use binary (base 2, zeros and ones, like almost all numbers in computers). So there is an exact number of binary digits in them, more in double and less in float. However, if you convert a decimal constant like 3.141592 to double, then print it with full accuracy, you don't get same number back. This is because converting the decimals between bases isn't exact in general (it's similar effect effect you get with 1/3 having infinite decimal expansion 0.33...). Example:

double pi = 3.141592;
std::cout << std::setprecision(100) << pi << std::endl;

Outputs for me:

3.14159200000000016217427400988526642322540283203125

So when you start multiplying this by 10 in your question code, you can see how it doesn't become an exact integer in a long time (and by then it is far beyond range of int), so your condition is never true (either there is fractional part, or the double is too large to fit in integer).

In other words, what you ask, counting digits directly from a double, makes no sense. You first need to print it as string, otherwise you don't really have a number of decimal digits to count. Of course you could micro-optimize this and skip the actual conversion to ASCII or UTF-16 string, but I don't see a scenario where that would be worth the trouble (except perhaps as a learning excercise).

If you need exact decimal digits in calculations, you need a special number type, which doesn't store fractions in binary. Example of such numeric type is Java BigDecimal.

hyde
  • 60,639
  • 21
  • 115
  • 176
  • Are you sure that you can actually get infinite expansions with base-2 floating point values? I'm pretty sure that you can't, and would likely be able to derive a proof for that (or simply find one on the internet)... (The reasoning would be along the lines of "Base-2 decimals are sums of `2^-n` which for each `n` has a finite base-10 reprentation, so the sum must have a finite number of digits, too.") – anderas Mar 21 '16 at 15:05
3

Atharva MR Fire here.

I've made a simple function that can count number of digits in a double.(float has 8 bits so just change 16 to 8 and make some other necessary changes.)

int get_float_digits(double num)
{
    int digits=0;
    double ori=num;//storing original number
    long num2=num;
    while(num2>0)//count no of digits before floating point
    {
        digits++;
        num2=num2/10;
    }
    if(ori==0)
        digits=1;
    num=ori;
    double no_float;
    no_float=ori*(pow(10, (16-digits)));
    long long int total=(long long int)no_float;
    int no_of_digits, extrazeroes=0;
    for(int i=0; i<16; i++)
    {
        int dig;
        dig=total%10;
        total=total/10;
        if(dig!=0)
            break;
        else
            extrazeroes++;
    }
    no_of_digits=16-extrazeroes;
    return no_of_digits;
}

If you want to get just the number of decimals, add a code no_of_digits=no_of_digits-digits; at the end, before the return function.

I hope this helps.

2
num = pi - int(pi);
while abs(num) >= 0.0000001{
    num = num * 10;
    count = count + 1;
    num = num - int(num);
}
cout<< count<< endl;

for more information, you can read this C/C++ counting the number of decimals?.

Community
  • 1
  • 1
Qingfu Wen
  • 41
  • 6
2
  1. Of coarse it is infinite loop

    When you are multiplying floating point variable with fractional part some numbers tend to behave as irrational on FPU arithmetics (due to rounding errors). So no matter how much you multiply by 10 there is still some fractional part ...

  2. how to do it instead

    You need to look at mantissa in binary instead and see where the least significant set bit is. so:

    union
        {
        double lf;
        BYTE db[8];
        } pi;
    
    pi.lf = 3.141592;
    // here just output the BYTES to memo,screen or whatever
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[0])); // LSB
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[1]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[2]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[3]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[4]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[5]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[6]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[7])); // MSB
    

    The result is that pi is stored as:

    0x400921FAFC8B007A hex
    0100 0000 0000 1001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
    

    According to Wiki IEEE_754 the double is divided like this:

    sign     = 0 bin -> +
    exponent = 100 0000 0000 bin - 1023 dec = 1
    mantissa = 1.1001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
    

    Note that mantissa has added 1. which is not present in the number representation !!!. Also the exponent is biased that is why the -1023 is there. Now when applied bit shift left by exponent we got

    pi = 11.001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
    

    Now we look for least set significant bit position

    pi = 11.001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
                                                                         ^
    

    It is at weight 2^-50=1/2^50=8.8817841970012523233890533447266e-16=~10e-16 (50th position after decimal point) meaning you got around ~16 decimal numbers of the rounded pi=3.141592 representation. When you print it properly you will see:

    3.14159200000000016
    

    You can also use n10=~log10(n2) to estimate the number of decadic decimals n10 safely represented by n2 binary decimals. But for low n2 is a bit irregular. for more info see

    From which for n2=50 is n10=15 which is pretty close to that ~16 decimals. Meaning that the 3.14159200000000016 should be truncated to 3.141592000000000 if just safe decadic digits may be present.

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380