Let's say I want to write a function that does the following:
Given a number N,
when N is rounded to D1 digits
does the result include more than D2 decimal places, not counting trailing zeroes?
For example, say N is .01001, D1 is 4, and D2 is 2. The question becomes, does .0100 include more than 2 decimal places, not counting trailing zeroes? And the answer is "no". But if N was .00101, the answer would be "yes".
I'm looking for an efficient way to do this using standard C library functions, taking into account the limitations of floating-point numbers.
An example of my intended usage: show a number using four digits if necessary, but otherwise show it using two digits.
(This is not a homework question -- this is a result of being a professional programmer who didn't do these kinds of homework questions when he was a student.)
The only easy way to do this with the standard library is to use snprintf (or just sprintf) with the right format, and then count the zeros yourself. Correctly computing the decimal representation of a (binary) floating point number is a very very difficult task that you don't want to try to do yourself, and you have near-zero chance of writing a correct version that's faster than your standard library one.
I hope I got this right; it's untested:
double n; /* the number N */
int d1, d2; /* the parameters d1 and d2 */
char s[MAXLEN], *z;
snprintf(s, sizeof s, "%.*f", d1, n);
for (z=s+strlen(s)-1; *z==0; z--);
if (strlen(++z)<d1-d2) puts("yes");
else puts("no");
Edit: As noted by ssianky, snprintf may have limitations on the precision it prints. Actually the C standard allows pretty much any floating point operation to give the wrong result for no reason whatsoever as long as the implementation documents as such, but IEEE behavior is encouraged, and POSIX additionally requires correctly rounded results up to DECIMAL_DIG places, but allows implementations to print nonsense (for example all zeros) after printing sufficiently many places to uniquely determine the actual floating point value. So to make a long story short, if d1 is reasonably large, or if your platform is pathological, snprintf-based approaches might not give the right answer. (In practice they'll give the right answer on GNU systems and the wrong answer on Microsoft systems.)
If you care about this shortcoming and want correct results for any value of d1, you'll have to implement exact float-to-decimal code yourself. Loops that involve multiplying a floating point value repeatedly by 10 will not suffice for this.
Edit 2: Looking at the OP's "intended usage", using snprintf seems like a no-brainer. If you want to print the value to begin with and are just trying to decide how many decimal places to use, just print it to a string and then chop off the trailing zeros before displaying it. In fact, the %g printf format specifier might even do what the OP wants already...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With