6

This is one interview question. How do you compute the number of digit after . in floating point number.

e.g. if given 3.554 output=3

for 43.000 output=0. My code snippet is here

double no =3.44;
int count =0;
while(no!=((int)no))
{
    count++;
    no=no*10;
}
printf("%d",count);

There are some numbers that can not be indicated by float type. for example, there is no 73.487 in float type, the number indicated by float in c is 73.486999999999995 to approximate it.

Now how to solve it as it is going in some infinite loop.

Note : In the IEEE 754 Specifications, a 32 bit float is divided as 24+7+1 bits. The 7 bits indicate the mantissa.

someone
  • 1,638
  • 3
  • 21
  • 36
  • 4
    So what *should* it do when you pass a non-ending decimal number to it? Should the program somehow know that `73.486999999999995` is "actually" `73.487`? Are you sure the decimal number should be a float, not a string representation? – JJJ Jul 24 '13 at 14:59
  • 2
    @Juhana: Assuming binary floating-point, all representable numbers are multiples of some power of two; no infinitely repeating rational numbers are representable. – Keith Thompson Jul 24 '13 at 15:01
  • @Juhana...if I give 73.487 it is going in infinite loop. I am not sure if I declare it as double and somehow it is converting to string. But why it should be a string representation? – someone Jul 24 '13 at 15:02
  • @Juhana The way to know it is that the floating point representations of both numbers are the same. So you want the shortest fraction that results in the same floating point. – Barmar Jul 24 '13 at 15:04
  • How would you handle `2.00000`? And `1.99999999999999999999999999`, as compiler will round it? – LS_ᴅᴇᴠ Jul 24 '13 at 15:19
  • 5
    @LS_dev: The result for `2.00000` (which is exactly representable) is simply 0. And a question isn't stupid just because the answer happens to be "you can't do that". It's a good question if the OP and other readers learn something from it. – Keith Thompson Jul 24 '13 at 15:28
  • 1
    This question states that a 32-bit `float` is partitioned into 24 bits, 7 bits, and 1 bit, and that the 7 bits specify the mantissa. This is incorrect. The 32-bit IEEE-754 binary floating-point format has one sign bit, eight bits for an exponent encoding, and 23 bits for a significand encoding. – Eric Postpischil Jul 24 '13 at 16:46
  • For a typical system, the 32-bit `float` number closest to `73.487` is exactly `73.48699951171875`. The closed 64-bit `double` number is exactly `73.486999999999994770405464805662631988525390625`. – Keith Thompson Feb 24 '21 at 18:53
  • I know it's old but, you can use floor-to-round value instead of casting. Casting to an int may not be accurate for very small values and may cause an infinite loop but the floor will not so you can use like while(no!=floor(num)). You should also include math.h "#include" & during compilation link it with -lm flag, ex: "gcc main.c -lm" – Pranav HS Feb 12 '23 at 09:59

8 Answers8

6

I doubt this is what you want since the question is asking for something that's not usually meaningful with floating point numbers, but here is the answer:

int digits_after_decimal_point(double x)
{
    int i;
    for (i=0; x!=rint(x); x+=x, i++);
    return i;
}
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
5

The problem isn't really solvable as stated, since floating-point is typically represented in binary, not in decimal. As you say, many (in fact most) decimal numbers are not exactly representable in floating-point.

On the other hand, all numbers that are exactly representable in binary floating-point are decimals with a finite number of digits -- but that's not particularly useful if you want a result of 2 for 3.44.

When I run your code snippet, it says that 3.44 has 2 digits after the decimal point -- because 3.44 * 10.0 * 10.0 just happens to yield exactly 344.0. That might not happen for another number like, say, 3.43 (I haven't tried it).

When I try it with 1.0/3.0, it goes into an infinite loop. Adding some printfs shows that no becomes exactly 33333333333333324.0 after 17 iterations -- but that number is too big to be represented as an int (at least on my system), and converting it to int has undefined behavior.

And for large numbers, repeatedly multiplying by 10 will inevitably give you a floating-point overflow. There are ways to avoid that, but they don't solve the other problems.

If you store the value 3.44 in a double object, the actual value stored (at least on my system) is exactly 3.439999999999999946709294817992486059665679931640625, which has 51 decimal digits in its fractional part. Suppose you really want to compute the number of decimal digits after the point in 3.439999999999999946709294817992486059665679931640625. Since 3.44 and 3.439999999999999946709294817992486059665679931640625 are effectively the same number, there's no way for any C function to distinguish between them and know whether it should return 2 or 51 (or 50 if you meant 3.43999999999999994670929481799248605966567993164062, or ...).

You could probably detect that the stored value is "close enough" to 3.44, but that makes it a much more complex problem -- and it loses the ability to determine the number of decimal digits in the fractional part of 3.439999999999999946709294817992486059665679931640625.

The question is meaningful only if the number you're given is stored in some format that can actually represent decimal fractions (such as a string), or if you add some complex requirement for determining which decimal fraction a given binary approximation is meant to represent.

There's probably a reasonable way to do the latter by looking for the unique decimal fraction whose nearest approximation in the given floating-point type is the given binary floating-point number.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Request elaboration on how $1.0/3.0=33333333333333324.0 $, I mean how can $2$ or $4$ occur even. Please elaborate though have read floating point representation too. – jiten Feb 23 '21 at 03:10
  • Floating-point numbers are stored in binary, not decimal. `0.33333333333333324` is an *approximate* decimal representation of the stored binary value. In binary, 1.0/3.0 would be 0.0101010101010101... (for however many bits are available in the representation). – Keith Thompson Feb 23 '21 at 03:18
3

The question could be interpreted as such:

Given a floating point number, find the shortest decimal representation that would be re-interpreted as the same floating point value with correct rounding.

Once formulated like this, the answer is Yes we can - see this algorithm:

Printing floating point numbers quickly and accurately. Robert G. Burger and R. Kent Dybvig. ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation, June 1996

http://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf

See also references from Compute the double value nearest preferred decimal result for a Smalltalk implementation.

Community
  • 1
  • 1
aka.nice
  • 9,100
  • 1
  • 28
  • 40
  • Does the use of intermediate types in floating-point parsing confuse things? In many .net languages, for example, attempting to parse the value 9007199791611905 as a `float` will not yield the closest `float` because it will first be converted to `double` (rounding it down to the next even integer), and that will then be rounded down again. I wonder if there are any `float` values whose shortest decimal representation would be correct if converted directly to `float`, but would yield incorrect results if parsed via `double`? – supercat Jul 25 '13 at 17:03
  • @supercat very difficult question... With many digits like your example, yes, we can build numbers such that rounding twice leads to a different float like http://stackoverflow.com/questions/13276862/c-c-notation-of-double-floating-point-values/13279512#13279512 but starting with minimal number of decimal digits for a Float, such number is not obvious to build. Does it even exists? – aka.nice Jul 25 '13 at 21:47
  • The particular number I mentioned is the smallest whole number for which which conversion via `double` yields something other than the closest `float`. I don't know of any particular minimal-length `float` values where double-rounding would cause trouble; I was wondering whether you either knew of any or knew with certainty that none exist. – supercat Jul 25 '13 at 22:08
  • 1
    @supercat After launching some loops, here's the smallest I found: 7.038531e-26f will produce a sequence of bits that rounds twice upper as in my example. – aka.nice Jul 26 '13 at 19:20
  • That would indeed seem to represent a problem case. That value, cast to double, then float, then double, is 0.0000003081487913E-26 higher than the original specified numeric quantity; the next lower float would be 0.0000003081487909 lower than original specified numeric quantity [i.e. it's a smidgin closer]. Which of those two float values would be printed as 7.038531e-26 by the linked algorithm? – supercat Jul 26 '13 at 20:12
  • @supercat the decimal value 7.038531e-26 is approximately 0x1.5C87FAFFFFFFFCE4F6700...p-21 the nearest double is 0x1.5C87FB0000000p-21 and the nearest float is 0x1.5C87FAp-21. Note that 0x1.5C87FA0000000p-21 is the nearest double to 7.038530691851209e-26 – aka.nice Jul 26 '13 at 21:03
2

Sounds like you need to either use sprintf to get an actual rounded version, or have the input be a string (and not parsed to a float).

Either way, once you have a string version of the number, counting characters after the decimal should be trivial.

Drew McGowen
  • 11,471
  • 1
  • 31
  • 57
2

It is my logic to count the number of digits. number = 245.98

  1. Take input as a string char str[10] = "245.98";
  2. Convert string to int using to count the number of digits before the decimal point. int atoi(const char *string)
  3. Use logic n/10 inside the while to count the numbers.
  4. Numbers after decimal logic
    • Get the length of the string using strlen(n)
    • inside the while (a[i]! ='.'). then increment i Later you can add step 3 logic output and step 4 logic output

Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char num[100] = "345653.8768";
    int count=0;
    int i=0;
    int len;
    int before_decimal = atoi(num);
    int after_decimal;
    int total_Count;
    printf("Converting string to int : %d\n", before_decimal);
    
    //Lets count the numbers of digits before before_decimal
    while(before_decimal!=0){
        before_decimal = before_decimal/10;
        count++;
    }

    printf("number of digits before decimal are %d\n",count);
    //Lets get the number of digits after decimal
    
    // first get the lenght of the string
    len = strlen(num);
    printf("Total number of digits including '.' are =%d\n",len);
    
    //Now count the number after '.' decimal points
    // Hope you know how to compare the strings
    while(num[i]!='.'){
       i++; 
    }
    // total lenght of number - numberof digits after decimal -1(becuase every string ends with '\0')
    after_decimal= len-i-1;
    printf("Number of digits after decimal points are %d\n",after_decimal);
    
    //Lets add both count Now
    // ie. Number of digits before decmal and after decimal
    
    total_Count = count+ after_decimal;
    
    printf("Total number of digits are :%d\n",total_Count);
    return 0;
}

Output:

Converting string to int : 345653                                                                                                                                                  
number of digits before decimal are 6                                                                                                                                              
Total number of digits including '.' are =11                                                                                                                                       
Number of digits after decimal points are 4                                                                                                                                        
Total number of digits are :10   

                 
Mahadev Gouda
  • 769
  • 11
  • 14
0

There are no general exact solutions. But you can convert the value to string and don't count the part exceeding the type's precision and exclude the trailing 0s or 9s. This will work for more cases but it still won't return the correct answer for all.

For example double's accuracy is about 15 digits if the input is a decimal string from the user (17 digits for binary-decimal-binary round trip), so for 73.486999999999995 there are 15 - 2 = 13 digits after the radix point (minus the 2 digits in the int part). After that there are still many 9s in the fractional part, subtract them from the count too. Here there are ten 9s which means there are 13 - 10 = 3 decimal digits. If you use 17 digits then the last digit which may be just garbage, exclude it before counting the 9s or 0s.

Alternatively just start from the 15 or 16th digit and iterate until you see the first non-0 and non-9 digit. Count the remaining digits and you'll get 3 in this case. Of course while iterating you must also make sure that the trailing is all 0s or all 9s

phuclv
  • 37,963
  • 15
  • 156
  • 475
0

Request: e.g. if given 3.554 output = 3, for 43.000 output = 0

Problem: that's already a decimal like 0.33345. When this gets converted to a double, it might be something like 0.333459999...125. The goal is merely to determine that 0.33345 is a shorter decimal that will produce the same double. The solution is to convert it to a string with the right number of digits that results in the same original value.

int digits(double v){
   int d=0; while(d < 50){
      string t=DoubleToString(v,d); double vt = StrToDouble(t);
      if(MathAbs(v-vt) < 1e-15) break;
      ++d;
   }
   return d;
}

double v=0.33345;    PrintFormat("v=%g, d=%i", v,digits(v));// v=0.33345, d=5
       v=0.01;       PrintFormat("v=%g, d=%i", v,digits(v));// v=0.01, d=2
       v=0.00001;    PrintFormat("v=%g, d=%i", v,digits(v));// v=1e-05, d=5
       v=5*0.00001;  PrintFormat("v=%g, d=%i", v,digits(v));// v=5e-05, d=5
       v=5*.1*.1*.1; PrintFormat("v=%g, d=%i", v,digits(v));// v=0.005, d=3
       v=0.05;       PrintFormat("v=%g, d=%i", v,digits(v));// v=0.05, d=2
       v=0.25;       PrintFormat("v=%g, d=%i", v,digits(v));// v=0.25, d=2
       v=1/3.;       PrintFormat("v=%g, d=%i", v,digits(v));// v=0.333333, d=15
phuclv
  • 37,963
  • 15
  • 156
  • 475
  • Welcome to stack overflow. While your post is ok, it's better if you don't paste HTML code in - the question editor has formatting controls, which allow you to highlight code blocks, quote blocks and do some simple formatting (bold/italics). Use these to make your post look consistent with other posts – Mikkel Dec 04 '16 at 19:55
-1

What you can do is multiply the number by various powers of 10, round that to the nearest integer, and then divide by the same number of powers of 10. When the final result compares different from the original number, you've gone one digit too far.

I haven't read it in a long time, so I don't know how it relates to this idea, but How to Print Floating-Point Numbers Accurately from PLDI 1990 and 2003 Retrospective are probably very relevant to the basic problem.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • I think I am doing that only...Is there any change what you said and what I did? – someone Jul 24 '13 at 15:08
  • 1
    Among other problems, if the input is the `double` nearest 1/3, this algorithm returns 16. Why? The `double` nearest 1/3 has 54 digits after the decimal point; it is 0.333333333333333314829616256247390992939472198486328125. It is not even close to a 16-digit decimal numeral (that is, its deviation from the nearest 16-digit decimal numeral is much larger than the value of a digit at that position). – Eric Postpischil Jul 24 '13 at 15:48
  • @EricPostpischil The input isn't a fraction like 1/3, it's something that's already a decimal like 0.33345. When this gets converted to a double, it might be something like 0.333459999...125. The goal is merely to determine that 0.33345 is a shorter decimal that will produce the same double. – Barmar Jul 24 '13 at 16:28
  • I've added links to some papers on printing of floating point numbers that discuss these ideas. – Barmar Jul 24 '13 at 16:36
  • 1
    @Barmar: You do not know what the input is because the question contains only a couple of examples, not a specification. As far as is stated, the input could be any representable value. This answer does not work for all representable values. – Eric Postpischil Jul 24 '13 at 16:44
  • @EricPostpischil The question says "If given 3.554" and "for 43.000". I interpreted that to mean that we input a string and it gets parsed into a float. – Barmar Jul 24 '13 at 16:47
  • 2
    @Barmar: If you are given a string and want to know the number of digits after the decimal point, then you should not parse it as a float at all. Just directly count the digits in the string. – Eric Postpischil Jul 24 '13 at 16:50