Avoid trailing zeroes in printf()

前端 未结 14 2182
猫巷女王i
猫巷女王i 2020-11-22 07:18

I keep stumbling on the format specifiers for the printf() family of functions. What I want is to be able to print a double (or float) with a maximum given number of digits

14条回答
  •  长情又很酷
    2020-11-22 07:32

    This can't be done with the normal printf format specifiers. The closest you could get would be:

    printf("%.6g", 359.013); // 359.013
    printf("%.6g", 359.01);  // 359.01
    

    but the ".6" is the total numeric width so

    printf("%.6g", 3.01357); // 3.01357
    

    breaks it.

    What you can do is to sprintf("%.20g") the number to a string buffer then manipulate the string to only have N characters past the decimal point.

    Assuming your number is in the variable num, the following function will remove all but the first N decimals, then strip off the trailing zeros (and decimal point if they were all zeros).

    char str[50];
    sprintf (str,"%.20g",num);  // Make the number.
    morphNumericString (str, 3);
    :    :
    void morphNumericString (char *s, int n) {
        char *p;
        int count;
    
        p = strchr (s,'.');         // Find decimal point, if any.
        if (p != NULL) {
            count = n;              // Adjust for more or less decimals.
            while (count >= 0) {    // Maximum decimals allowed.
                 count--;
                 if (*p == '\0')    // If there's less than desired.
                     break;
                 p++;               // Next character.
            }
    
            *p-- = '\0';            // Truncate string.
            while (*p == '0')       // Remove trailing zeros.
                *p-- = '\0';
    
            if (*p == '.') {        // If all decimals were zeros, remove ".".
                *p = '\0';
            }
        }
    }
    

    If you're not happy with the truncation aspect (which would turn 0.12399 into 0.123 rather than rounding it to 0.124), you can actually use the rounding facilities already provided by printf. You just need to analyse the number before-hand to dynamically create the widths, then use those to turn the number into a string:

    #include 
    
    void nDecimals (char *s, double d, int n) {
        int sz; double d2;
    
        // Allow for negative.
    
        d2 = (d >= 0) ? d : -d;
        sz = (d >= 0) ? 0 : 1;
    
        // Add one for each whole digit (0.xx special case).
    
        if (d2 < 1) sz++;
        while (d2 >= 1) { d2 /= 10.0; sz++; }
    
        // Adjust for decimal point and fractionals.
    
        sz += 1 + n;
    
        // Create format string then use it.
    
        sprintf (s, "%*.*f", sz, n, d);
    }
    
    int main (void) {
        char str[50];
        double num[] = { 40, 359.01335, -359.00999,
            359.01, 3.01357, 0.111111111, 1.1223344 };
        for (int i = 0; i < sizeof(num)/sizeof(*num); i++) {
            nDecimals (str, num[i], 3);
            printf ("%30.20f -> %s\n", num[i], str);
        }
        return 0;
    }
    

    The whole point of nDecimals() in this case is to correctly work out the field widths, then format the number using a format string based on that. The test harness main() shows this in action:

      40.00000000000000000000 -> 40.000
     359.01335000000000263753 -> 359.013
    -359.00999000000001615263 -> -359.010
     359.00999999999999090505 -> 359.010
       3.01357000000000008200 -> 3.014
       0.11111111099999999852 -> 0.111
       1.12233439999999995429 -> 1.122
    

    Once you have the correctly rounded value, you can once again pass that to morphNumericString() to remove trailing zeros by simply changing:

    nDecimals (str, num[i], 3);
    

    into:

    nDecimals (str, num[i], 3);
    morphNumericString (str, 3);
    

    (or calling morphNumericString at the end of nDecimals but, in that case, I'd probably just combine the two into one function), and you end up with:

      40.00000000000000000000 -> 40
     359.01335000000000263753 -> 359.013
    -359.00999000000001615263 -> -359.01
     359.00999999999999090505 -> 359.01
       3.01357000000000008200 -> 3.014
       0.11111111099999999852 -> 0.111
       1.12233439999999995429 -> 1.122
    

提交回复
热议问题