How do I restrict a float value to only two places after the decimal point in C?

前端 未结 17 2696
孤城傲影
孤城傲影 2020-11-22 03:22

How can I round a float value (such as 37.777779) to two decimal places (37.78) in C?

相关标签:
17条回答
  • 2020-11-22 03:46
    printf("%.2f", 37.777779);
    

    If you want to write to C-string:

    char number[24]; // dummy size, you should take care of the size!
    sprintf(number, "%.2f", 37.777779);
    
    0 讨论(0)
  • 2020-11-22 03:49

    There isn't a way to round a float to another float because the rounded float may not be representable (a limitation of floating-point numbers). For instance, say you round 37.777779 to 37.78, but the nearest representable number is 37.781.

    However, you can "round" a float by using a format string function.

    0 讨论(0)
  • 2020-11-22 03:49

    You can still use:

    float ceilf(float x); // don't forget #include <math.h> and link with -lm.
    

    example:

    float valueToRound = 37.777779;
    float roundedValue = ceilf(valueToRound * 100) / 100;
    
    0 讨论(0)
  • 2020-11-22 03:50

    In C++ (or in C with C-style casts), you could create the function:

    /* Function to control # of decimal places to be output for x */
    double showDecimals(const double& x, const int& numDecimals) {
        int y=x;
        double z=x-y;
        double m=pow(10,numDecimals);
        double q=z*m;
        double r=round(q);
    
        return static_cast<double>(y)+(1.0/m)*r;
    }
    

    Then std::cout << showDecimals(37.777779,2); would produce: 37.78.

    Obviously you don't really need to create all 5 variables in that function, but I leave them there so you can see the logic. There are probably simpler solutions, but this works well for me--especially since it allows me to adjust the number of digits after the decimal place as I need.

    0 讨论(0)
  • 2020-11-22 03:50

    Always use the printf family of functions for this. Even if you want to get the value as a float, you're best off using snprintf to get the rounded value as a string and then parsing it back with atof:

    #include <math.h>
    #include <stdio.h>
    #include <stddef.h>
    #include <stdlib.h>
    
    double dround(double val, int dp) {
        int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
        char *buffer = malloc(charsNeeded);
        snprintf(buffer, charsNeeded, "%.*f", dp, val);
        double result = atof(buffer);
        free(buffer);
        return result;
    }
    

    I say this because the approach shown by the currently top-voted answer and several others here - multiplying by 100, rounding to the nearest integer, and then dividing by 100 again - is flawed in two ways:

    • For some values, it will round in the wrong direction because the multiplication by 100 changes the decimal digit determining the rounding direction from a 4 to a 5 or vice versa, due to the imprecision of floating point numbers
    • For some values, multiplying and then dividing by 100 doesn't round-trip, meaning that even if no rounding takes place the end result will be wrong

    To illustrate the first kind of error - the rounding direction sometimes being wrong - try running this program:

    int main(void) {
        // This number is EXACTLY representable as a double
        double x = 0.01499999999999999944488848768742172978818416595458984375;
    
        printf("x: %.50f\n", x);
    
        double res1 = dround(x, 2);
        double res2 = round(100 * x) / 100;
    
        printf("Rounded with snprintf: %.50f\n", res1);
        printf("Rounded with round, then divided: %.50f\n", res2);
    }
    

    You'll see this output:

    x: 0.01499999999999999944488848768742172978818416595459
    Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
    Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
    

    Note that the value we started with was less than 0.015, and so the mathematically correct answer when rounding it to 2 decimal places is 0.01. Of course, 0.01 is not exactly representable as a double, but we expect our result to be the double nearest to 0.01. Using snprintf gives us that result, but using round(100 * x) / 100 gives us 0.02, which is wrong. Why? Because 100 * x gives us exactly 1.5 as the result. Multiplying by 100 thus changes the correct direction to round in.

    To illustrate the second kind of error - the result sometimes being wrong due to * 100 and / 100 not truly being inverses of each other - we can do a similar exercise with a very big number:

    int main(void) {
        double x = 8631192423766613.0;
    
        printf("x: %.1f\n", x);
    
        double res1 = dround(x, 2);
        double res2 = round(100 * x) / 100;
    
        printf("Rounded with snprintf: %.1f\n", res1);
        printf("Rounded with round, then divided: %.1f\n", res2);
    }
    

    Our number now doesn't even have a fractional part; it's an integer value, just stored with type double. So the result after rounding it should be the same number we started with, right?

    If you run the program above, you'll see:

    x: 8631192423766613.0
    Rounded with snprintf: 8631192423766613.0
    Rounded with round, then divided: 8631192423766612.0
    

    Oops. Our snprintf method returns the right result again, but the multiply-then-round-then-divide approach fails. That's because the mathematically correct value of 8631192423766613.0 * 100, 863119242376661300.0, is not exactly representable as a double; the closest value is 863119242376661248.0. When you divide that back by 100, you get 8631192423766612.0 - a different number to the one you started with.

    Hopefully that's a sufficient demonstration that using roundf for rounding to a number of decimal places is broken, and that you should use snprintf instead. If that feels like a horrible hack to you, perhaps you'll be reassured by the knowledge that it's basically what CPython does.

    0 讨论(0)
提交回复
热议问题