views:

487

answers:

3

How do I create a string so that it formats floating point numbers to have no trailing decimal point or digits when it is an integer, but to NOT switch to scientific notation for larger numbers?

When I do:

    float myFloat= 15.6f;
    float myInt = 5.0f;
    float myLarge = 7000000.0f;
    sprintf(out, "my float is %g", myFloat);
    sprintf(out, "my int is %g", myInt);
    sprintf(out, "my large is %g", myLarge);

I get something like:

    my float is 15.6
    my int is 5
    my large is 7e+07f

I want all a single format string that will give 15.6, 5, and 700000.

Edited cause comments don't do formatting:

that's what I thought. but a wrapper is pretty inconvenient though as the format strings are embedded in longer format strings:

    sprintf(buf, "%d %g", myInt, myFloat);

how do you wrap that?

    sprintf(buf, "%d %g", myInt, Wrapper(myFloat));??

what should Wrapper return? Or worse:

    sprintf(buf, "%d %s", myInt, Wrapper(myFloat));?? 
+3  A: 

You can format with "%.*f", which allows you to pass the precision as a separate parameter to e.g. sprintf. I've used this in the past when I wanted to display 0, 1, 2, or maybe 3 digits nicely without extra trailing 0's. Write a little helper function which takes the double/float and returns an integer precision (by multiplying by 1000 and then doing modulo 10, 100, 1000). Then call sprintf with "%.*f", call the helper function, and pass the float.

Mike Kale
+1 This is really neat! I didn't know about this.
rampion
+2  A: 

I don't think you can get this with %g, as %g gives you the shortest shorter of %e or %f.

So, I don't think there a single printf argument that satisfies you. For 15.6, you could use %2.1f, and for 7000000, you could use %7.0f.

Your best bet is probably to write a wrapper function that would look at the size of you value, and apply the correct printf argument.

Joce
yeah that's what I thought. that pretty inconvenient though as the format strings are embedded in longer format strings: > sprintf(buf, "%d %g", myInt, myFloat);how do you wrap that? > sprintf(buf, "%d %g", myInt, Wrapper(myFloat));??what should Wrapper return? Or worse: > sprintf(buf, "%d %s", myInt, Wrapper(myFloat));??
sean riley
Here's a though: Write a my_sprintf. Check for a token you define (something like %y or whatever), do you replace, and call sprintf underneath with your replaced string in place. This way, you'd have my_sprintf(buf, "%d %y", myInt, myFloat);
Joce
You could use Mike Kale's trick (%.*f) in your my_sprintf instead of passing an actual string.
Joce
A: 

Mike's approach is reasonable, but you can compute the number of digits directly using the log10 function, and use either floor or ceil to verify that you in fact have an integer value. Something like:

#include <math.h>
double x;
...
if (floor(x) == x && x != 0.0) { // x is an non-zero integer value
    int digits = floor(log10(fabs(x)+0.5)) + 1;
    printf("x is %.*f\n", digits, x);
}

Unpacking that chain of function calls, we first take the absolute value to make sure the argument is non-negative, add 0.5 to the known integer value to take care of any possible roundoff errors in computing log10 of an exact power of 10, combine log10 and floor to get the exponent of the largest power of 10 less than fabs(x)+0.5, and finally add 1 to get the number of digits needed to represent x. Here are some sample values (from OpenOffice, but the C log10 function should calculate in similar precision):

         x          log10(x+0.5)
         1  0.176091259055681000
         9  0.977723605288848000
        10  1.021189299069940000
        99  1.997823080745730000
       100  2.002166061756510000
       999  2.999782798454140000
      1000  3.000217092972230000
     10000  4.000021714181250000
    100000  5.000002171466980000
   1000000  6.000000217147190000
  10000000  7.000000021714720000
 100000000  8.000000002171470000
1000000000  9.000000000217150000
dewtell
I think the OP wants the number of digits after the decimal point, not the order of the number. 1 + floor(log10(1.234 + 0.5)) is 1, not the (I think) desired 3.
Mike Kale
Yeah, I missed that the point of Mike's successive mod operations was to figure out how many places to print after the decimal point for non-integer values. I was focusing on the part of Sean's request that there be no trailing digits or decimal point for integral values. You can combine our two approaches to figure out the digits before and after the decimal point in order to compute the overall precision needed.
dewtell