Formatting float without decimals

Discussion in 'C Programming' started by marc, Jan 25, 2014.

  1. marc

    marc Guest

    Hello,

    Is it possible to format a float number, but without displaying decimals
    if it is an integer ?

    For example with "%.2f",
    I get "10.50" for 10.5, but I would like "10" for 10 and not "10.00"

    Thanks.
     
    marc, Jan 25, 2014
    #1
    1. Advertisements

  2. marc

    marc Guest

    marc a écrit :
    Well, it seems "%.3g" works fine for my need
     
    marc, Jan 25, 2014
    #2
    1. Advertisements

  3. marc

    Les Cargill Guest


    there's always brute force.


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

    int main(void)
    {
    static char tehtmp[40];
    sprintf(tehtmp,"%.2f",aNumber);

    const static char * const dotzerozero = ".00";
    float aNumber = 10.0;

    char * const tt = strstr(tehtmp,dotzerozero);

    if (tt != NULL)
    {
    *tt = '\0';
    // or memset(tt,0,strlen(dotzerozero));
    }

    printf("%s",tehtmp);
    return 0;
    }
     
    Les Cargill, Jan 25, 2014
    #3
  4. float f = 3.142;

    if ((float)((int)f) == f) printf("%d", (int)f);
    else printf("%.2f", f);

    Best regards,
    Rick C. Hodgin
     
    Rick C. Hodgin, Jan 25, 2014
    #4
  5. This can cause an overflow since the range of float is often much wider
    than the range of int. You could convert to long long int, but I think
    there is a better way to do the test: use round (or roundf if a float
    argument is really required).

    I find the extraneous cast and parentheses get in the way of seeing what
    the test does (but I know not everyone agrees). I.e. I'd write

    if ((int)f == f) ...

    Also, the conditional can also be avoided:

    printf("%.*f", roundf(f) == f ? 0 : 2, f);
     
    Ben Bacarisse, Jan 25, 2014
    #5
  6. It's good that you have a solution, but it does not do what you said you
    wanted!
     
    Ben Bacarisse, Jan 25, 2014
    #6
  7. marc

    Kaz Kylheku Guest

    It does; however, he should be using a much higher precision so as not
    to lose significant digits. I would propose, say:

    "%." DBL_DIG "g"

    This approach is exactly how this works:

    $ txr -e '(format t "~s" 3.0)'
    3.0
    $ txr -e '(format t "~a" 3.0)'
    3

    In the case of "~s", the ".0" is actually *added* deliberately, so that there
    is print/read consistency: the printed representation is a floating-point
    number.

    How this works is that there is a sprintf with a "%.*g" conversion, where
    the argument to the * is a variable precision. Its default value is DBL_DIG.

    But, of course %g will use either %f or %e as appropriate, and so we
    can get results like:

    $ txr -p '(format nil "~a" 0.000000000000000001)'
    1e-18

    You can see this in the vformat function in this source file:
    http://www.kylheku.com/cgit/txr/tree/stream.c
    Look for "case 'a'".

    Here is the thing. Although an approximate description of %g is that it works
    by using the formatting of either %f or %e, this is not exactly true: %f does
    not have the %g behavior of removing trailing zeros in the fractional part,
    including, possibly the decimal separator itself. %f also interprets the
    precision parameter differently: precision to %f represents the number of
    digits after the decimal point, whereas %g treats precision as the number of
    significant figures.

    If you must always have output in the form \d+([.]\d+)? where the (\d+)? part
    is omitted if the digits are all zero, and has no trailing zeros, then
    use sprintf to format the number into a buffer, and do some text processing
    on it. You can integrate it into printf like this:

    char scratch[64]; /* or whatever */

    printf("%s\n", format_num(3.0, 5, scratch)); /* up to 5 digits past point */

    format_num returns a pointer into the scratch buffer where it placed the
    formatted number. You can further adjust that with some width and precision
    on the %s.s. format_num can be written so that it (perhaps optionally)
    obliterates the unwanted decimal parts with spaces:

    Hypothetical run:

    /* 1 tells format: "replace trailing stuff with spaces; do not chop". */

    printf("%10s\n", format_num(3.14, 5, 1, scratch));
    printf("%10s\n", format_num(3.0, 5, 1, scratch));

    Output: right adjusted in a field of 10 by %s:

    ___3.14___ <- underscores represent spaces
    ___3______

    The point is that the numbers are still nicely aligned.

    Possible implementation of format_num:

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

    /* scratch is assumed to be 64 bytes */
    char *format_num(double val, int fracdig, int use_spaces, char *scratch)
    {
    int chars = snprintf(scratch, 64, "%.*f", fracdig, val);
    char *dot = strchr(scratch, '.');

    if (chars < 0 || /* old, nonconforming sprintf libraries */
    chars >= 64)
    return "#.#"; /* could not format number */

    if (!dot)
    return scratch;

    fracdig = strlen(dot+1); /* these should be equal, but just in case */

    if (strspn(dot+1, "0") == fracdig) {
    /* decimal is all zeros */
    if (use_spaces)
    memset(dot, ' ', strlen(dot));
    else
    *dot = 0;
    } else {
    int nonzeros = strcspn(dot+1, "0");
    if (nonzeros < fracdig) {
    if (use_spaces)
    memset(dot + 1 + nonzeros, ' ', fracdig - nonzeros);
    else
    *(dot + 1 + nonzeros) = 0;
    }
    }

    return scratch;
    }

    int main(void)
    {
    char sch[64];

    /* pitiful test suite */
    printf("[%10s]\n", format_num(3.0, 5, 1, sch));
    printf("[%10s]\n", format_num(3.14, 5, 1, sch));
    printf("[%10s]\n", format_num(3.0, 5, 0, sch));
    printf("[%10s]\n", format_num(3.14, 5, 0, sch));
    printf("[%10s]\n", format_num(3.0, 0, 1, sch));
    printf("[%10s]\n", format_num(3.14, 0, 1, sch));
    printf("[%10s]\n", format_num(3.0, 0, 0, sch));
    printf("[%10s]\n", format_num(3.14, 0, 0, sch));
    printf("[%10s]\n", format_num(3.14e50, 0, 0, sch));
    printf("[%10s]\n", format_num(3.14e120, 0, 0, sch));

    return 0;
    }

    Output:

    [ 3 ]
    [ 3.14 ]
    [ 3]
    [ 3.14]
    [ 3]
    [ 3]
    [ 3]
    [ 3]
    [314000000000000011495964840544938881872998818643968]
    [ #.#]
     
    Kaz Kylheku, Jan 25, 2014
    #7
  8. marc

    James Kuyper Guest

    Just use "%.0f".

    "For a, A, e, E, f, F, g, and G conversions ... a decimal-point
    character appears in the result of these conversions only if a digit
    follows it." (7.21.6.1p6)
     
    James Kuyper, Jan 26, 2014
    #8
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.