round() for float in C++

后端 未结 22 1170
时光取名叫无心
时光取名叫无心 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:25

    It may be worth noting that if you wanted an integer result from the rounding you don't need to pass it through either ceil or floor. I.e.,

    int round_int( double r ) {
        return (r > 0.0) ? (r + 0.5) : (r - 0.5); 
    }
    
    0 讨论(0)
  • 2020-11-22 03:25

    I use the following implementation of round in asm for x86 architecture and MS VS specific C++:

    __forceinline int Round(const double v)
    {
        int r;
        __asm
        {
            FLD     v
            FISTP   r
            FWAIT
        };
        return r;
    }
    

    UPD: to return double value

    __forceinline double dround(const double v)
    {
        double r;
        __asm
        {
            FLD     v
            FRNDINT
            FSTP    r
            FWAIT
        };
        return r;
    }
    

    Output:

    dround(0.1): 0.000000000000000
    dround(-0.1): -0.000000000000000
    dround(0.9): 1.000000000000000
    dround(-0.9): -1.000000000000000
    dround(1.1): 1.000000000000000
    dround(-1.1): -1.000000000000000
    dround(0.49999999999999994): 0.000000000000000
    dround(-0.49999999999999994): -0.000000000000000
    dround(0.5): 0.000000000000000
    dround(-0.5): -0.000000000000000
    
    0 讨论(0)
  • 2020-11-22 03:26

    The C++03 standard relies on the C90 standard for what the standard calls the Standard C Library which is covered in the draft C++03 standard (closest publicly available draft standard to C++03 is N1804) section 1.2 Normative references:

    The library described in clause 7 of ISO/IEC 9899:1990 and clause 7 of ISO/IEC 9899/Amd.1:1995 is hereinafter called the Standard C Library.1)

    If we go to the C documentation for round, lround, llround on cppreference we can see that round and related functions are part of C99 and thus won't be available in C++03 or prior.

    In C++11 this changes since C++11 relies on the C99 draft standard for C standard library and therefore provides std::round and for integral return types std::lround, std::llround :

    #include <iostream>
    #include <cmath>
    
    int main()
    {
        std::cout << std::round( 0.4 ) << " " << std::lround( 0.4 ) << " " << std::llround( 0.4 ) << std::endl ;
        std::cout << std::round( 0.5 ) << " " << std::lround( 0.5 ) << " " << std::llround( 0.5 ) << std::endl ;
        std::cout << std::round( 0.6 ) << " " << std::lround( 0.6 ) << " " << std::llround( 0.6 ) << std::endl ;
    }
    

    Another option also from C99 would be std::trunc which:

    Computes nearest integer not greater in magnitude than arg.

    #include <iostream>
    #include <cmath>
    
    int main()
    {
        std::cout << std::trunc( 0.4 ) << std::endl ;
        std::cout << std::trunc( 0.9 ) << std::endl ;
        std::cout << std::trunc( 1.1 ) << std::endl ;
    
    }
    

    If you need to support non C++11 applications your best bet would be to use boost round, iround, lround, llround or boost trunc.

    Rolling your own version of round is hard

    Rolling your own is probably not worth the effort as Harder than it looks: rounding float to nearest integer, part 1, Rounding float to nearest integer, part 2 and Rounding float to nearest integer, part 3 explain:

    For example a common roll your implementation using std::floor and adding 0.5 does not work for all inputs:

    double myround(double d)
    {
      return std::floor(d + 0.5);
    }
    

    One input this will fail for is 0.49999999999999994, (see it live).

    Another common implementation involves casting a floating point type to an integral type, which can invoke undefined behavior in the case where the integral part can not be represented in the destination type. We can see this from the draft C++ standard section 4.9 Floating-integral conversions which says (emphasis mine):

    A prvalue of a floating point type can be converted to a prvalue of an integer type. The conversion truncates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot be represented in the destination type.[...]

    For example:

    float myround(float f)
    {
      return static_cast<float>( static_cast<unsigned int>( f ) ) ;
    }
    

    Given std::numeric_limits<unsigned int>::max() is 4294967295 then the following call:

    myround( 4294967296.5f ) 
    

    will cause overflow, (see it live).

    We can see how difficult this really is by looking at this answer to Concise way to implement round() in C? which referencing newlibs version of single precision float round. It is a very long function for something which seems simple. It seems unlikely that anyone without intimate knowledge of floating point implementations could correctly implement this function:

    float roundf(x)
    {
      int signbit;
      __uint32_t w;
      /* Most significant word, least significant word. */
      int exponent_less_127;
    
      GET_FLOAT_WORD(w, x);
    
      /* Extract sign bit. */
      signbit = w & 0x80000000;
    
      /* Extract exponent field. */
      exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;
    
      if (exponent_less_127 < 23)
        {
          if (exponent_less_127 < 0)
            {
              w &= 0x80000000;
              if (exponent_less_127 == -1)
                /* Result is +1.0 or -1.0. */
                w |= ((__uint32_t)127 << 23);
            }
          else
            {
              unsigned int 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. */
            return x + x;
          else
            return x;
        }
      SET_FLOAT_WORD(x, w);
      return x;
    }
    

    On the other hand if none of the other solutions are usable newlib could potentially be an option since it is a well tested implementation.

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

    There's no round() in the C++98 standard library. You can write one yourself though. The following is an implementation of round-half-up:

    double round(double d)
    {
      return floor(d + 0.5);
    }
    

    The probable reason there is no round function in the C++98 standard library is that it can in fact be implemented in different ways. The above is one common way but there are others such as round-to-even, which is less biased and generally better if you're going to do a lot of rounding; it's a bit more complex to implement though.

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

    Function double round(double) with the use of the modf function:

    double round(double x)
    {
        using namespace std;
    
        if ((numeric_limits<double>::max() - 0.5) <= x)
            return numeric_limits<double>::max();
    
        if ((-1*std::numeric_limits<double>::max() + 0.5) > x)
            return (-1*std::numeric_limits<double>::max());
    
        double intpart;
        double fractpart = modf(x, &intpart);
    
        if (fractpart >= 0.5)
            return (intpart + 1);
        else if (fractpart >= -0.5)
            return intpart;
        else
            return (intpart - 1);
        }
    

    To be compile clean, includes "math.h" and "limits" are necessary. The function works according to a following rounding schema:

    • round of 5.0 is 5.0
    • round of 3.8 is 4.0
    • round of 2.3 is 2.0
    • round of 1.5 is 2.0
    • round of 0.501 is 1.0
    • round of 0.5 is 1.0
    • round of 0.499 is 0.0
    • round of 0.01 is 0.0
    • round of 0.0 is 0.0
    • round of -0.01 is -0.0
    • round of -0.499 is -0.0
    • round of -0.5 is -0.0
    • round of -0.501 is -1.0
    • round of -1.5 is -1.0
    • round of -2.3 is -2.0
    • round of -3.8 is -4.0
    • round of -5.0 is -5.0
    0 讨论(0)
  • 2020-11-22 03:30

    Based on Kalaxy's response, the following is a templated solution that rounds any floating point number to the nearest integer type based on natural rounding. It also throws an error in debug mode if the value is out of range of the integer type, thereby serving roughly as a viable library function.

        // round a floating point number to the nearest integer
        template <typename Arg>
        int Round(Arg arg)
        {
    #ifndef NDEBUG
            // check that the argument can be rounded given the return type:
            if (
                (Arg)std::numeric_limits<int>::max() < arg + (Arg) 0.5) ||
                (Arg)std::numeric_limits<int>::lowest() > arg - (Arg) 0.5)
                )
            {
                throw std::overflow_error("out of bounds");
            }
    #endif
    
            return (arg > (Arg) 0.0) ? (int)(r + (Arg) 0.5) : (int)(r - (Arg) 0.5);
        }
    
    0 讨论(0)
提交回复
热议问题