Printing floats, not doubles, with printf functions

In MSDN, the format of printf states that %f is for printing doubles.
The format of scanf, on the other hand, makes the difference between %f for floats and %lf for doubles.

Is there a way I can print a float variable on the screen so that only its correct value is printed? That is to say, it is well-known that only 7 digits of any float number are relevant, thus, for example, 957.3456 is okay but 19923.3456 is not (the latter should be 19923.35). But if I assign the latter to some float variable, or make some operation with this result, and then printf it (or fprintf, or sprintf) it will show all the digits.

So, here is the question: if I'm using a log file and print numbers into it with fprintf(LOG_FILE,"%f",fX), how should I change is in order to get in the file the same value that I'm getting in Visual Studio's watch? That is, if I watch the value of fX during the debug, the value is different from that written into LOG_FILE, because of what I said above.
LVL 1
LeschaAsked:
Who is Participating?
 
waysideConnect With a Mentor Commented:
I don't see any way to do it just with the format specifications. And who's to knwo what sort of manipulations Visual Studiois doing before they show it; there's no guarantee they are using a simple printf.

You could do something like this though:

char buf[50];
sprintf(buf, "%f", myfloat);
pdot = strchr(buf, '.');  // find where the '.' is
result = (int)(pdot - buf); // find the index of the '.'

int trunc = 8;
if (result > 8)
  trunc = result; // ends string at the '.', i.e. 12345670000.000 => 12345670000
else if (result == 1 && buf[0] == '0') {
  // we have a 0.  find the first non-zero after the decimal, add 7
  int i = 2;
  while (buf[i] == '0') {
     i++;
  }
  trunc = i + 7;
}

buf[trunc] = '\0'; // leaves 7 digits plus the '.'

// buf now contains 7 significant digits
printf(buf);

Seems a lot easier then messing with logs and mantissas and what-not.
0
 
ssnkumarCommented:
What about the following statement:
printf("%7.2f\n", i); // i is of type float

-ssnkumar
0
 
mokuleCommented:
You can specify precision that decimal digits by for example %.2f to two digits.
It can also be a parameter %.*f. Then You should pass two values: printed and precision.
0
Introducing Cloud Class® training courses

Tech changes fast. You can learn faster. That’s why we’re bringing professional training courses to Experts Exchange. With a subscription, you can access all the Cloud Class® courses to expand your education, prep for certifications, and get top-notch instructions.

 
LeschaAuthor Commented:
No, that's not what I want, because I don't know how my digits are situated around the decimal point.
For example,

123456789 is not a good float.
1234567.89 is not a good float, either.
0.123456789 is not a good float, either.

See my point?

The only way I can see to do that is to print it to a string, like a double and with sprintf, and then look for the decimal point, etc. But this is tedious and, honestly, stupid. Surely there should be another way to do this?>
0
 
ssnkumarCommented:
But, you can write a small code to find out the number of digits before the decimal point and use that result for further processing...!?

-ssnkumar
0
 
LeschaAuthor Commented:
How, exactly?
And what further processing?
0
 
mokuleCommented:

something like
  if( x <> 0)
    printf("%.*f",7-round(log10(abs(x)),x);
0
 
ssnkumarCommented:
int getNoOfDigits(int n)
{
        int i = 0;

        if (n == 0) return 1;

        while (n)
        {
                n = n / 10;
                i++;
        }

        return i;
}
0
 
ozoCommented:
fprintf(LOG_FILE,"%.7g",fX),
      
0
 
stefan73Commented:
Hi Lescha,

First of all, float arguments to a vargs function are promoted to double. So %f is fine.

Toy around with this:

#include <stdio.h>
#include <math.h>


#define MANTISSA_PRECISION 7
#define P(x) { int px = (int)(MANTISSA_PRECISION-1-floor(log10(fabs(x)))); \
      printf("%-20s: px=%2d %.*f\n",#x,px,px,(float)x);}

int main(){
      P(1.234567);
      P(0.0001234567);
      P(12.34567);
      P(1234567);
      P(0.1234567);
      return 0;
}


Cheers,
Stefan
0
 
mokuleCommented:
Hi, Stefan
Looks very similar to my example.
Shoudn't check for x==0 ?
Marek
0
 
ozoCommented:
How about
P(123e20);
0
 
stefan73Commented:
mokule,
> Looks very similar to my example.
Yes :-)

But your example used abs() instead of fabs(), which is a common error - I fell into this pitfall once, too...

Stefan
0
 
mokuleCommented:
Stefan,
Your absolutly right. :-)
Marek
0
 
stefan73Commented:
ozo,
You're right. The macro should be defined as
#define P(x) { int px = (int)(MANTISSA_PRECISION-1-floor(log10(fabs(x)))); \
      if(px<0) px=0; \
      printf("%-20s: px=%2d %.*f\n",#x,px,px,(float)x);}

...otherwise there will six decimal places if px < 0.

BTW: It's interesting to see the difference between double and float mantissa precision in the 123e20 case:

123e20              : px= 0 12300000492793395937280 (float)
123e20              : px= 0 12300000000000001048576 (double)
123e20              : px= 0 12300000000000001048576 (long double -> %Lf)
123e20L             : px= 0 12300000000000000000000 (long double)
123e30L             : px= 0 123000000000000000000494994980864 (long double)

Lescha: This "random noise" beyond mantissa accuracy is not avoidable.

Stefan
0
 
LeschaAuthor Commented:
Guys, I think you either misread my question or I didn't make it quite clear. What I want is to get the exact same representation in a file/console/string as I get in the Visual Studio's watch window during debug. Somehow, they do know exactly what to show me. I want to know, too...
0
 
ssnkumarCommented:
Did you try out my suggestion?

-ssnkumar
0
 
LeschaAuthor Commented:
ssnkumar:
Your suggestion is not connected to the issue. You talk about integers, not floats.

wayside:
I tested it and it is as close to what I need as can be, I think. Thanks.
0
 
stefan73Commented:
Lescha,
> Somehow, they do know exactly what to show me

OK, it's simple:

#define P(x) { int px = (int)(MANTISSA_PRECISION-1-floor(log10(fabs(x)))); \
      if(px<0) px=0;  \
      else if (px > MANTISSA_PRECISION) px=MANTISSA_PRECISION; \
      printf("%-20s: px=%3d %.*g\n",#x,px,MANTISSA_PRECISION,(float)x);}

The maximum width of the output will be MANTISSA_PRECISION + 1 (decimal point) + 4 (like e+05).

Stefan
0
 
stefan73Commented:
wayside,
> Seems a lot easier then messing with logs and mantissas and what-not.
That's exactly what you're doing ;-)

For large numbers, the solution you offered has no limit in digits. A buffer size of 50 is very prone to overflows (snprintf!).

Stefan
0
 
LeschaAuthor Commented:
Thanks...
0
 
waysideCommented:
> For large numbers, the solution you offered has no limit in digits. A buffer size of 50 > is very prone to overflows (snprintf!).

Very true. For floats (range of 1e38 to 1e-38 or so, 50 would be enough. For a double (1e308 to 1e-308 or so) you'd need a larger buffer, 350 or so should be enough. And you're right, it's always better to use snprintf.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.