0

I have to count the number of digits after the decimal. I found a code but it's not working for some numbers. like for 2.12 its giving output as 7 instead of 2 but for 2.125 its giving output as 3 which is correct.

#include<stdio.h>
int main()
{
    float no =2.34;
    int count =0;
    while(no!=((int)no))
    {
        count++;
        no=no*10;
    }
    printf("\t %d \n",count);
    return 0;
}

Tell me if it's anyhow possible to find out the number of digits after the decimal point in C.

Daniel Walker
  • 6,380
  • 5
  • 22
  • 45
  • 3
    It isn't possible to do it reliably unless the fractional part ends with a 5, and even that's subject to caveats. – Jonathan Leffler May 21 '20 at 04:11
  • 2
    The problem is that 2.34 can't be represented exactly in binary floating point. See https://stackoverflow.com/questions/588004/is-floating-point-math-broken – Nate Eldredge May 21 '20 at 04:11
  • @JonathanLeffler using strings is an option or not? – shubham1355 May 21 '20 at 04:13
  • convert float to char array and count number after decimal appears – Ferin Patel May 21 '20 at 04:14
  • 2
    It depends. If you format it with `%.3f` you get one answer; if you format it with `%.6f`, you will often get a different answer. Is that OK? – Jonathan Leffler May 21 '20 at 04:14
  • 1
    *"Is it possible to count the number of digits after decimal point in C?"* No, it's not possible. – user3386109 May 21 '20 at 04:19
  • The comments above are true when not using an infinite-precision library. Those kind of libraries are still plain C language I suppose... – linuxfan says Reinstate Monica May 21 '20 at 05:27
  • @linuxfan The question is whether you can determine the number of digits used to create a `float` with a declaration like `float no = 2.34;`. The answer is "no" because there are multiple possible digit strings on the right hand side of that assignment that will result in the exact same bit pattern in the variable. So the information was lost at the time of the assignment, and an infinite-precision library won't help recover that information. – user3386109 May 21 '20 at 06:06
  • Try `while (x - (int)x > 0.001)`? see https://ideone.com/ptuLWE – pmg May 21 '20 at 07:12
  • @user3386109 I don't see, in the question, the statement "with a declaration like *float no = ...*". You are true, the problem lies in the assignment, but this is a limit of binary representation, not C language. If you write "float a = 0.123456789123456678456" you already know that you are making a mistake, and not because the C language fault. – linuxfan says Reinstate Monica May 21 '20 at 07:22
  • @pmg Fails instantly with `x = 0.0001` – user3386109 May 21 '20 at 07:37
  • 1
    @linuxfansaysReinstateMonica: Yes - it's unfortunate that the example provided wasn't more like `int count_digits_after_decimal_point(float value) {` so that people could focus on the actual question asked. – Brendan May 22 '20 at 10:48

3 Answers3

-1
#include <stdio.h>
#include <string.h>
int main()
{
   float no = 2.3948;
   int i = 0;
   int startCounting = 0;
   int count = 0;

   char c[500];
   sprintf(c, "%g", no);

   for(i = 0;i<strlen(c);i++)
   {
       if (startCounting == 1)
       {
           count ++;
       }
       if(c[i] == '.')
       {
           startCounting = 1;
       }
   }
   printf("\nTotal Count: %d", count);
}
Ferin Patel
  • 3,424
  • 2
  • 17
  • 49
  • `float no = 0.1234567` gives me a count of `6`. I suggest you re-read the comments under the main post to see why the question has no good answer. – dxiv May 21 '20 at 04:38
  • It gives 6 as answer because float holds upto 6 decimal places – Ferin Patel May 21 '20 at 04:41
  • 1
    No, they have 6 to 9 significant digits. Anyway, change that to `double` and your code still returns `6`. Once you get past the formatting issues, you'll hit the real issue, which is that many/most decimal numbers do not have an exact representation in binary-based IEEE-754, so what's stored in the variable `no` is not *exactly* the decimal number you typed-in in the source code. That makes trying to count decimals pointless and hopeless. – dxiv May 21 '20 at 04:44
  • Here's a simpler example: `0.00001` Give that a try with your code. – user3386109 May 21 '20 at 04:55
  • @dxiv: [There is no sensible sense in which `float` (binary32) objects have 6-9 decimal digits.](https://stackoverflow.com/a/61614323/298225) – Eric Postpischil May 21 '20 at 10:12
  • @dxiv: You're talking about the conversion of decimal to float, which is irrelevant (but imprecise). What is relevant is the opposite - conversion of float to decimal; which can be precise, and has guaranteed bounds (e.g. https://en.wikipedia.org/wiki/Machine_epsilon ). – Brendan May 21 '20 at 10:38
  • @EricPostpischil Good reading, thanks for the pointer. That said, I meant significant digits, not identical digits. – dxiv May 21 '20 at 15:46
  • 1
    @Brendan *Both* conversions are relevant here. When the code is compiled, what gets stored in the variable `no` is the result of a decimal-to-ieee-754 conversion performed by the compiler. At runtime the compiled binary has no memory of the exact decimal value that was originally entered in the source code. – dxiv May 21 '20 at 15:46
  • @dxiv: No. The question is asking about one conversion only (but happened to provide a "throw-away example" that was effected by the other conversion because the asker wasn't aware of problems with the irrelevant conversion). The only correct answer to the actual question is "Yes, that's perfectly possible" (with additional notes to ensure the asker is aware of problems with the irrelevant conversion). In real code the values are likely to come from elsewhere (e.g. the result of calculations, etc) and will not be baked into the source code. – Brendan May 22 '20 at 10:32
-1

Binary floating-point is designed to approximate real-number arithmetic. It is not designed to record information about decimal digits. If you want to work with decimal arithmetic, you should use a format other than the float or double types of your compiler.

For float, your C implementation most likely uses the IEEE-754 basic 32-bit binary format, also called binary32. The number 2.34 cannot be represented in this format. In float no =2.34;, the source text 2.34 is a double constant, and the decimal “2.34” is converted to the nearest value representable in a double, which is 2.339999999999999857891452847979962825775146484375, if your C implementation uses the IEEE-754 binary64 format and uses round-to-nearest-ties-to-even, both of which are common. This double value is then used to initialize no, which is a float, so it is converted to the nearest value representable in the float format, which is 2.3399999141693115234375.

If your code correctly counted the number of decimal digits after the decimal point required to represent this number, it would print 22. One reason it does not is because the operation no*10 produces more rounding errors. In binary, ten is 10102, and multiplying by it increases the number of bits needed to represent result without rounding error. Since float has a fixed number of bits, it is not possible to use more bits. So the real-number result is rounded to fit. Each time your program executes no=no*10, it loses some information about the number.

Consider that both float no = 2.34; and float no = 2.3399999141693115234375; yield exactly the same result in no. Therefore, no difference between them can be detected from that value. So no software could tell whether the numeral with two digits after the decimal point or the numeral with 22 digits after the decimal point had been used to initialize no.

Therefore, no software can tell you how many digits that original numeral had, if all it has to use is the value in no.

It is possible to figure out the shortest decimal numeral that would produce a particular float value. Java and JavaScript provide this in their default formatting for floating-point numbers. Then one could count the digits in that numeral. However, this is useful only for situations where a floating-point result is known to be equal to the result of converting some numeral with a limited number of decimal digits to binary floating-point. If the floating-point result comes from calculations, we cannot expect it to be near a short decimal numeral. For example, if no came from other calculations and were just a little higher or lower, the float value would be one of the neighboring values, 2.340000152587890625 or 2.339999675750732421875, near decimal numerals “2.3400002” and “2.3399997”. Generally, when working with binary floating-point arithmetic, we should not expect to get simple decimal results.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
-2

Is it possible to count the number of digits after decimal point in C?

Yes.

For integers it's easy (always zero). For fixed point numbers you can use a lookup table with either the fractional part as index (smaller code, larger table) or the "bit number" of the lowest set bit as the index (larger code, smaller table). This includes both "big integer" and primitive types.

For floating point types; you have to figure out what you actually want. Options are:

  • the number of actual digits in the raw value. In this case use the "bit number of lowest set bit in significand, adjusted by exponent, as index into table" approach (with safeguards for infinities and NaNs).

  • the number of useful digits. In this case you have to analyze the source code to determine "worst case error", or write code that keeps track of error at run-time to determine "actual case error", for all things leading up to the value you have. Then you can use a similar "lowest set bit of significand adjusted by exponent and error, as index into table" approach.

  • the number of digits that would be used for a specific format specifier (e.g. in printf()). In this case start by using sprintf() to convert it into a string. For %f and similar, and %a and similar, count "digit chars after the decimal point (if there is one)" in the resulting string. For %e and similar, count "digits between decimal point (if there is one) and first non-digit char after decimal point", then check for an exponent and parse it separately, then combine both results by using subtraction (e.g. 1.23e-9 would be 2-(-9) = 11) then clamp the result to a non-negative number (e.g. 1.23e+9 would be 2-(+9) = -7 = no digits).

For big rational numbers (e.g. constructed from three big integers, as "numerator/divisor << exponent"); you have to figure out what you actually want. Options are:

  • the number of actual digits in the raw value. This is "very complex" (e.g. for a number like 1/3 the answer can be infinity).

  • the number of useful digits. In this case you probably have to analyze the source code to determine "worst case error" and use that to adjust "number of actual digits in raw value". Note that operations on big rational numbers typically don't have any error at all; but there will be error from conversions from values used to create/initialize the big rational numbers.

  • the number of digits that would be used if the value was printed. This depends on the code that prints it, but you can probably use a similar approach (convert to string, then parse the string) as floating point.

WARNING: Don't be misled by people in the comments. There are 2 issues:

  • converting decimal into floating point; which is imprecise and can create "un-intuitive" results if you're not aware of it; but is not technically relevant for the question you asked.

  • converting floating point back into decimal (and counting digits after the decimal point); which is (can be) precise. This is the only thing your question is asking for; and the answer is that it's entirely possible (and not necessarily hard).

Note that your original code does work correctly for some floating point values (e.g. does work correctly for the value 2.12 if you forgive rounding error - Eric's answer is right about that); and you only thought it wasn't working because of the unrelated "converting decimal into floating point" problem. However; your original code will not work correctly for values that are too large to fit in an int. To handle all values a float can have (e.g. including FLT_MAX) you'd need an integer type with around 150 bits, which doesn't exist as a primitive type in C, so you'd need to use some kind of "big integer" code/library or the method I described.

To be more specific; every "combination of bits" that can be stored in a float is either an infinity, a NaN, a zero, a "normal" that has a number of digits after the decimal point that can be determined exactly, or is a "subnormal" that has a number of digits after the decimal point that can be determined exactly; with the limit being the smallest magnitude non-zero value (approximately 1.4013e-45 assuming IEEE-754) which has the most digits after the decimal point that any float can have.

Brendan
  • 35,656
  • 2
  • 39
  • 66
  • "*converting decimal into floating point ... is not technically relevant for the question*" But it *is* entirely relevant, see my other comment [here](https://stackoverflow.com/questions/61927111/is-it-possible-to-count-the-number-of-digits-after-decimal-point-in-c/61927270?noredirect=1#comment109548892_61927270). That is, unless the input is provided as a string, not a floating point value, but in that case counting decimals would be a trivial task. – dxiv May 21 '20 at 18:18
  • @dxiv: No. The question is literally asking about "counting digits after decimal point" which only requires conversion from something (float) to decimal and does not involve the opposite irrelevant conversion at all. It's "superfluous surrounding info" that is irrelevant to the question itself that involves the opposite conversion. – Brendan May 22 '20 at 10:36
  • I agree that the assignment problem is not technically relevant to the question, and a program made like that would be useless. One should know (and anyway has to live with it) that C uses floating point, which can not treat decimal numbers like humans do. We all have seen those results like "2.999999" and so on, and we all accept it. So converting a float to string and counting the digits *is the solution*. – linuxfan says Reinstate Monica May 22 '20 at 12:49
  • @Brendan The question as stated has a floating point value as input. Unless the user provides the exact value in the bit format native to the target platform, that value is the result of a decimal to floating point conversion, whether at compile time (`double val = ...`) or runtime (`val = atof(...)`). Discarding that step as "superfluous surrounding info" changes the question IMHO. This is the same point I was making in previous comments, and I'll leave it at that. – dxiv May 22 '20 at 16:36
  • @dxiv: If you're going to ignore everything that matters (including the literal question asked, which is "Is it possible to count the number of digits after decimal point in C?" and doesn't mention floating point or restrict itself to "floating point only") and focus on the example in isolation; then the answer is "Don't bother, if you hard-code the value in the source code then you should hard-code the result in the source code too, and only do a single `printf("\nTotal Count:2");` with no other code". Obviously, focusing on the example in isolation (and ignoring the question) is idiotic. – Brendan May 23 '20 at 21:11
  • @dxiv: Instead; you have to assume that the example is just an example, the value is hard-coded as a temporary convenience for testing only, and that it's far more likely that the asker cares about values that are not hard-coded at all (e.g. results of calculations based on data obtained at run-time from disk/network/user). – Brendan May 23 '20 at 21:16