round() for float in C++

后端 未结 22 1172
时光取名叫无心
时光取名叫无心 2020-11-22 03:01

I need a simple floating point rounding function, thus:

double round(double);

round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1

I can find

相关标签:
22条回答
  • 2020-11-22 03:31

    It's usually implemented as floor(value + 0.5).

    Edit: and it's probably not called round since there are at least three rounding algorithms I know of: round to zero, round to closest integer, and banker's rounding. You are asking for round to closest integer.

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

    You could round to n digits precision with:

    double round( double x )
    {
    const double sd = 1000; //for accuracy to 3 decimal places
    return int(x*sd + (x<0? -0.5 : 0.5))/sd;
    }
    
    0 讨论(0)
  • 2020-11-22 03:31

    If you need to be able to compile code in environments that support the C++11 standard, but also need to be able to compile that same code in environments that don't support it, you could use a function macro to choose between std::round() and a custom function for each system. Just pass -DCPP11 or /DCPP11 to the C++11-compliant compiler (or use its built-in version macros), and make a header like this:

    // File: rounding.h
    #include <cmath>
    
    #ifdef CPP11
        #define ROUND(x) std::round(x)
    #else    /* CPP11 */
        inline double myRound(double x) {
            return (x >= 0.0 ? std::floor(x + 0.5) : std::ceil(x - 0.5));
        }
    
        #define ROUND(x) myRound(x)
    #endif   /* CPP11 */
    

    For a quick example, see http://ideone.com/zal709 .

    This approximates std::round() in environments that aren't C++11-compliant, including preservation of the sign bit for -0.0. It may cause a slight performance hit, however, and will likely have issues with rounding certain known "problem" floating-point values such as 0.49999999999999994 or similar values.

    Alternatively, if you have access to a C++11-compliant compiler, you could just grab std::round() from its <cmath> header, and use it to make your own header that defines the function if it's not already defined. Note that this may not be an optimal solution, however, especially if you need to compile for multiple platforms.

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

    There is no need to implement anything, so I'm not sure why so many answers involve defines, functions, or methods.

    In C99

    We have the following and and header <tgmath.h> for type-generic macros.

    #include <math.h>
    double round (double x);
    float roundf (float x);
    long double roundl (long double x);
    

    If you cannot compile this, you have probably left out the math library. A command similar to this works on every C compiler I have (several).

    gcc -lm -std=c99 ...
    

    In C++11

    We have the following and additional overloads in #include <cmath> that rely on IEEE double precision floating point.

    #include <math.h>
    double round (double x);
    float round (float x);
    long double round (long double x);
    double round (T x);
    

    There are equivalents in the std namespace too.

    If you cannot compile this, you may be using C compilation instead of C++. The following basic command produces neither errors nor warnings with g++ 6.3.1, x86_64-w64-mingw32-g++ 6.3.0, clang-x86_64++ 3.8.0, and Visual C++ 2015 Community.

    g++ -std=c++11 -Wall
    

    With Ordinal Division

    When dividing two ordinal numbers, where T is short, int, long, or another ordinal, the rounding expression is this.

    T roundedQuotient = (2 * integerNumerator + 1)
        / (2 * integerDenominator);
    

    Accuracy

    There is no doubt that odd looking inaccuracies appear in floating point operations, but this is only when the numbers appear, and has little to do with rounding.

    The source is not just the number of significant digits in the mantissa of the IEEE representation of a floating point number, it is related to our decimal thinking as humans.

    Ten is the product of five and two, and 5 and 2 are relatively prime. Therefore the IEEE floating point standards cannot possibly be represented perfectly as decimal numbers for all binary digital representations.

    This is not an issue with the rounding algorithms. It is mathematical reality that should be considered during the selection of types and the design of computations, data entry, and display of numbers. If an application displays the digits that show these decimal-binary conversion issues, then the application is visually expressing accuracy that does not exist in digital reality and should be changed.

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

    As pointed out in comments and other answers, the ISO C++ standard library did not add round() until ISO C++11, when this function was pulled in by reference to the ISO C99 standard math library.

    For positive operands in [½, ub] round(x) == floor (x + 0.5), where ub is 223 for float when mapped to IEEE-754 (2008) binary32, and 252 for double when it is mapped to IEEE-754 (2008) binary64. The numbers 23 and 52 correspond to the number of stored mantissa bits in these two floating-point formats. For positive operands in [+0, ½) round(x) == 0, and for positive operands in (ub, +∞] round(x) == x. As the function is symmetric about the x-axis, negative arguments x can be handled according to round(-x) == -round(x).

    This leads to the compact code below. It compiles into a reasonable number of machine instructions across various platforms. I observed the most compact code on GPUs, where my_roundf() requires about a dozen instructions. Depending on processor architecture and toolchain, this floating-point based approach could be either faster or slower than the integer-based implementation from newlib referenced in a different answer.

    I tested my_roundf() exhaustively against the newlib roundf() implementation using Intel compiler version 13, with both /fp:strict and /fp:fast. I also checked that the newlib version matches the roundf() in the mathimf library of the Intel compiler. Exhaustive testing is not possible for double-precision round(), however the code is structurally identical to the single-precision implementation.

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <string.h>
    #include <math.h>
    
    float my_roundf (float x)
    {
        const float half = 0.5f;
        const float one = 2 * half;
        const float lbound = half;
        const float ubound = 1L << 23;
        float a, f, r, s, t;
        s = (x < 0) ? (-one) : one;
        a = x * s;
        t = (a < lbound) ? x : s;
        f = (a < lbound) ? 0 : floorf (a + half);
        r = (a > ubound) ? x : (t * f);
        return r;
    }
    
    double my_round (double x)
    {
        const double half = 0.5;
        const double one = 2 * half;
        const double lbound = half;
        const double ubound = 1ULL << 52;
        double a, f, r, s, t;
        s = (x < 0) ? (-one) : one;
        a = x * s;
        t = (a < lbound) ? x : s;
        f = (a < lbound) ? 0 : floor (a + half);
        r = (a > ubound) ? x : (t * f);
        return r;
    }
    
    uint32_t float_as_uint (float a)
    {
        uint32_t r;
        memcpy (&r, &a, sizeof(r));
        return r;
    }
    
    float uint_as_float (uint32_t a)
    {
        float r;
        memcpy (&r, &a, sizeof(r));
        return r;
    }
    
    float newlib_roundf (float x)
    {
        uint32_t w;
        int exponent_less_127;
    
        w = float_as_uint(x);
        /* Extract exponent field. */
        exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;
        if (exponent_less_127 < 23) {
            if (exponent_less_127 < 0) {
                /* Extract sign bit. */
                w &= 0x80000000;
                if (exponent_less_127 == -1) {
                    /* Result is +1.0 or -1.0. */
                    w |= ((uint32_t)127 << 23);
                }
            } else {
                uint32_t exponent_mask = 0x007fffff >> exponent_less_127;
                if ((w & exponent_mask) == 0) {
                    /* x has an integral value. */
                    return x;
                }
                w += 0x00400000 >> exponent_less_127;
                w &= ~exponent_mask;
            }
        } else {
            if (exponent_less_127 == 128) {
                /* x is NaN or infinite so raise FE_INVALID by adding */
                return x + x;
            } else {
                return x;
            }
        }
        x = uint_as_float (w);
        return x;
    }
    
    int main (void)
    {
        uint32_t argi, resi, refi;
        float arg, res, ref;
    
        argi = 0;
        do {
            arg = uint_as_float (argi);
            ref = newlib_roundf (arg);
            res = my_roundf (arg);
            resi = float_as_uint (res);
            refi = float_as_uint (ref);
            if (resi != refi) { // check for identical bit pattern
                printf ("!!!! arg=%08x  res=%08x  ref=%08x\n", argi, resi, refi);
                return EXIT_FAILURE;
            }
            argi++;
        } while (argi);
        return EXIT_SUCCESS;
    }
    
    0 讨论(0)
  • 2020-11-22 03:33

    Since C++ 11 simply:

    #include <cmath>
    std::round(1.1)
    

    or to get int

    static_cast<int>(std::round(1.1))
    
    0 讨论(0)
提交回复
热议问题