round() for float in C++

423,830

Solution 1

It's available since C++11 in cmath (according to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf)

#include <cmath>
#include <iostream>

int main(int argc, char** argv) {
  std::cout << "round(0.5):\t" << round(0.5) << std::endl;
  std::cout << "round(-0.5):\t" << round(-0.5) << std::endl;
  std::cout << "round(1.4):\t" << round(1.4) << std::endl;
  std::cout << "round(-1.4):\t" << round(-1.4) << std::endl;
  std::cout << "round(1.6):\t" << round(1.6) << std::endl;
  std::cout << "round(-1.6):\t" << round(-1.6) << std::endl;
  return 0;
}

Output:

round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2

Solution 2

Editor's Note: The following answer provides a simplistic solution that contains several implementation flaws (see Shafik Yaghmour's answer for a full explanation). Note that C++11 includes std::round, std::lround, and std::llround as builtins already.

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.

Solution 3

Boost offers a simple set of rounding functions.

#include <boost/math/special_functions/round.hpp>

double a = boost::math::round(1.5); // Yields 2.0
int b = boost::math::iround(1.5); // Yields 2 as an integer

For more information, see the Boost documentation.

Edit: Since C++11, there are std::round, std::lround, and std::llround.

Solution 4

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.

Solution 5

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); 
}
Share:
423,830

Related videos on Youtube

Roddy
Author by

Roddy

Updated on January 19, 2022

Comments

  • Roddy
    Roddy over 2 years

    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 ceil() and floor() in the math.h - but not round().

    Is it present in the standard C++ library under another name, or is it missing??

    • Frank
      Frank about 13 years
      If you just want to output the number as a rounded number it seems you can just do std::cout << std::fixed << std::setprecision(0) << -0.9, for example.
    • Shog9
      Shog9 about 13 years
      Protecting this... New users with brilliant new rounding schemes should read existing answers first.
    • Alessandro Jacopson
      Alessandro Jacopson about 11 years
      round is available since C++11 in <cmath>. Unfortunately if you are in Microsoft Visual Studio it is still missing: connect.microsoft.com/VisualStudio/feedback/details/775474/…
    • Shafik Yaghmour
      Shafik Yaghmour almost 10 years
      As I note in my answer, rolling your own round has a lot of caveats. Before C++11, the standard relied on C90 which did not include round. C++11 relies on C99 which does have round but also as I noted includes trunc which has different properties and may be more appropriate depending on the application. Most answers also seem to ignore that a user may wish to return an integral type which has even more issues.
    • Shafik Yaghmour
      Shafik Yaghmour almost 10 years
      @uvts_cvs this does not seem to be an issue with the latest version of visual studio, see it live.
    • Alessandro Jacopson
      Alessandro Jacopson almost 10 years
      @ShafikYaghmour you are right, Microsoft declare it as "Closed as Fixed" but without info about the release of MSVC or the date of the fix :-(
    • simplename
      simplename about 2 years
      for basic usage, simply static_cast<int>(std::round(0.1)), for more details there are the answers
  • xtofl
    xtofl over 15 years
    It's good to make the distinction between different versions of 'round'. It's good to know when to pick which, too.
  • Registered User
    Registered User almost 15 years
    This doesn't handle negative numbers correctly. The answer by litb is correct.
  • Roddy
    Roddy almost 15 years
    @InnerJoin: Yes, it handles negative numbers differently to litb's answer, but that doesn't make it "incorrect".
  • Lazer
    Lazer almost 14 years
    What if you want to round to some places after the decimal precision? multiply and divide?
  • Andreas Magnusson
    Andreas Magnusson almost 14 years
    @Lazer: I would be careful to mul-div floating-point values. There's no guarantee that (f * 10) / 10 == f for a floating-point value. That said, a mul-div is probably the easiest way to achieve it...
  • Daniel Wolf
    Daniel Wolf about 13 years
    Boost also offers a set of simple rounding functions; see my answer.
  • aka.nice
    aka.nice almost 12 years
    Unless your compiler int size defaults to 1024 bits, this ain't gonna be accurate for huge double...
  • Carl
    Carl almost 12 years
    I think that is acceptable given when it will be used: If your double value is 1.0 e+19, rounding out to 3 places doesn't make sense.
  • aka.nice
    aka.nice almost 12 years
    sure, but the question is for a generic round, and you can't control how it will be used. There is no reason for round to fail where ceil and floor would not.
  • aka.nice
    aka.nice almost 12 years
    This is a good solution. I'm not sure that rounding -1.5 to -1.0 is standard though, I would expect -2.0 by symetry. Also I don't see the point of the leading guard, the first two if could be removed.
  • aka.nice
    aka.nice almost 12 years
    I checked in ISO/IEC 10967-2 standard, open-std.org/jtc1/sc22/wg11/docs/n462.pdf and from appendix B.5.2.4, the rounding function must indeed be symmetric, rounding_F(x) = neg_F(rounding_F(neg_F(x)))
  • Pete855217
    Pete855217 over 11 years
    Didn't you mean pow(10,place) rather than the binary operator ^ in 10^place? 10^2 on my machine gives me 8!! Nevertheless on my Mac 10.7.4 and gcc, the code doesn't work, returning the original value.
  • Pascal Cuoq
    Pascal Cuoq almost 11 years
    Adding 0.5 before truncating fails to round to the nearest integer for several inputs including 0.49999999999999994. See blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
  • Sergi0
    Sergi0 over 10 years
    @Roddy no, the fact that it rounds negative numbers incorrect makes it incorrect.
  • Waihon Yew
    Waihon Yew over 10 years
    @Sergi0: There is no "correct" and "incorrect" because there are more than one definitions of rounding that decide what happens at the halfway point. Check your facts before passing judgement.
  • stijn
    stijn over 10 years
    Does not give the expected result for 0.49999999999999994 though (well, depending on what you expect of course, but 0 seems more reasonable to me than 1)
  • stijn
    stijn over 10 years
    You can also use boost:numeric::RoundEven< double >::nearbyint directly if you don't want to-integer. @DanielWolf note that the simple function is implemented using +0.5 which has problems as layed out by aka.nice
  • kalaxy
    kalaxy over 10 years
    @stijn Good catch. I found that adding the long double literal suffix to my constants fixed your example issue, but I don't know if there are other precision examples that it wouldn't catch.
  • stijn
    stijn over 10 years
    see aka.nice's answer and the links provided - try with 5000000000000001.0 for example
  • stijn
    stijn over 10 years
    btw if you add 0.49999999999999994 instead of 0.5, it does work ok for both 0.49999999999999994 and 5000000000000001.0 as input. Not sure if it is ok for all values though, and I couldn't find any reference stating that this is the ultimate fix.
  • Pascal Cuoq
    Pascal Cuoq over 10 years
    @stijn It is ok for all values, if you do not care in which direction the values exactly in-between two integers are rounded. Without thinking, I would prove it by case analysis with the following cases: 0 <= d < 0.5, 0.5 <= d < 1.5, 1.5 <= d < 2^52, d >= 2^52. I also exhaustively tested the single-precision case.
  • Muhammad
    Muhammad over 10 years
    you can check std::numeric_limits::round_style for round style type and to implement your function for accurate result, in c++11 you should use std::fegetround to know round mode. see en.cppreference.com/w/cpp/types/numeric_limits/round_style and en.cppreference.com/w/cpp/numeric/fenv/feround
  • SSpoke
    SSpoke over 10 years
    Very important bug since it doesn't use ceil if you do round(-2.5) it will give back -2 instead of -3.
  • Andreas Magnusson
    Andreas Magnusson over 10 years
    @SSpoke: It rounds up to the nearest larger integer. -2 > -3. It's called "Round Half Up": en.wikipedia.org/wiki/Rounding#Round_half_up
  • Andreas Magnusson
    Andreas Magnusson about 10 years
    @MuhammadAnnaqeeb: You're right, things have improved immensely since the release of C++11. This question was asked and answered in another time when life was hard and the joys were few. It remains here as an ode to heroes who lived and fought back then and for those poor souls who still are unable to use modern tools.
  • Gustavo Maciel
    Gustavo Maciel almost 10 years
    I was already using boost in my project, +1 for this, much better than using the naïve floor(value + 0.5) approach!
  • Shafik Yaghmour
    Shafik Yaghmour almost 10 years
    @Roddy considering Pascal Couq's three part article, which is in a comment above, on how tricky this operation is this answer is just not sufficient. Any answer that purports to roll your own would have to be very long indeed. The 20+ downvotes should also be a strong indication that something is wrong with this answer.
  • Shafik Yaghmour
    Shafik Yaghmour almost 10 years
    As I pointed out in my answer adding 0.5 does not work in all cases. Although at least you deal with the overflow issue so you avoid undefined behavior.
  • Shafik Yaghmour
    Shafik Yaghmour almost 10 years
    @downvoter please explain what can be improved? The vast majority of the answer here are just wrong since they attempt to roll their own round which all fail in one form or another. If there is something missing in my explanation please let me know.
  • aka.nice
    aka.nice almost 10 years
    I'm interested to know about the downvotes. Is it because the tie is resolved away from zero rather than to nearest even?
  • truthseeker
    truthseeker over 9 years
    Result value should be floating point value with double precision.
  • truthseeker
    truthseeker over 9 years
    The resulting value should be floating point value with double precision. Its not an answer for question asked.
  • Aleksey F.
    Aleksey F. over 9 years
    @ truthseeker: Yeah, I had to see the required type of return value. OK, see "UPD".
  • sp2danny
    sp2danny about 9 years
    there is also lround and llround for integral results
  • Tony Delroy
    Tony Delroy almost 9 years
    Per 4.9 [conv.fpint], "The behavior is undefined if the truncated value cannot be represented in the destination type.", so this is a little dangerous. Other SO answers describe how to do this robustly.
  • chux - Reinstate Monica
    chux - Reinstate Monica over 8 years
    Nice complete answer - especially the just below 0.5 part. Another niche: round(-0.0). C spec does not appear to specify. I'd expect -0.0 as a result.
  • chux - Reinstate Monica
    chux - Reinstate Monica over 8 years
    Note: the C spec says "rounding halfway cases away from zero, regardless of the current rounding direction.", so rounding without regard to odd/even is compliant.
  • Ruslan
    Ruslan about 8 years
    @chux interesting, and IEEE 754-2008 standard does specify that rounding preserves signs of zeros and infinities (see 5.9).
  • Ruslan
    Ruslan about 8 years
    @Shafik this is a great answer. I've never thought that even rounding is a non-trivial operation.
  • Bruce Dawson
    Bruce Dawson almost 8 years
    There are indeed different rounding algorithms which can all make reasonable claims to being "correct". However floor(value + 0.5) is not one of these. For some values, such as 0.49999997f or the equivalent double, the answer is just wrong - it will be rounded to 1.0 when all agree that it should be zero. See this post for details: blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
  • chux - Reinstate Monica
    chux - Reinstate Monica over 7 years
    Using LONG_MIN-0.5 and LONG_MAX+0.5 introduces complications as the math may not be exact. LONG_MAX may exceed double precision for exact conversion. Further likely want assert(x < LONG_MAX+0.5); (< vs <=) as LONG_MAX+0.5 may be exactly representable and (x)+0.5 may have an exact result of LONG_MAX+1 which fails long cast. Other corner issues too.
  • Aaron3468
    Aaron3468 over 7 years
    @Jon You're partially correct. The three major types of rounding are floor, ceiling, and truncation, with the term round often reserved specifically as a synonym of truncation. Given the ubiquity of the prior 3 rounding functions, I'd expect a standard math library in any language to support their use (as many do). Anything beyond those lies in the realm where I would not fault language designers for expecting users to design specialized libraries; most languages are general-purpose.
  • Douglas Daseeco
    Douglas Daseeco almost 7 years
    This function is incorrect for negative numbers. Furthermore, no one living on earth and connected to the Internet should be using libraries from the last century when backward and forward compatibility have been so carefully considered in the last 20 years. See stackoverflow.com/questions/485525/round-for-float-in-c/….
  • Peter Cordes
    Peter Cordes over 6 years
    @sp2danny: or better, lrint to use the current rounding mode instead of round's funky away-from-zero tiebreak.
  • Peter Cordes
    Peter Cordes over 6 years
    Perhaps worth mentioning that std::rint() is often preferable to std::round() when C++11 is available for numeric and performance reasons. It uses the current rounding mode, unlike round()'s special mode. It can be much more efficient on x86, where rint can inline to a single instruction. (gcc and clang do that even without -ffast-math godbolt.org/g/5UsL2e, while only clang inlines the nearly-equivalent nearbyint()) ARM has single-instruction support for round(), but on x86 it can only inline with multiple instructions, and only with -ffast-math
  • Peter Cordes
    Peter Cordes over 6 years
    Don't call your function round(double), there's already a standard math library function of that name (in C++11) so it's confusing. Use std::lrint(x) if it's available.
  • Peter Cordes
    Peter Cordes over 6 years
    This has undefined behaviour for args outside the range of int. (In practice on x86, out-of-range FP values will make CVTTSD2SI produce 0x80000000 as the integer bit pattern, i.e. INT_MIN, which will then be converted back to double.
  • Peter Cordes
    Peter Cordes over 6 years
    This has undefined behaviour for args outside the range of int. (In practice on x86, out-of-range FP values will make CVTTSD2SI produce 0x80000000 as the integer bit pattern, i.e. INT_MIN, which will then be converted back to double.
  • Peter Cordes
    Peter Cordes over 6 years
    This is going to be slow compared to C++11 rint() or nearbyint(), but if you really can't use a compiler that provides a proper rounding function, and you need precision more than performance...
  • Peter Cordes
    Peter Cordes over 6 years
    The compiler will hopefully inline rint() or nearbyint() to an SSE4.1 roundsd instruction or an x87 frndint instruction, which will be much faster than the two store/reload round trips needed to use this inline asm on data in a register. MSVC inline asm sucks quite a lot for wrapping single instructions like frndint because there's no way to get the input in a register. Using it at the end of a function with the result in st(0) might be reliable as a way of returning output; apparently that's safe for eax for integers, even when it inlines the function containing the asm.
  • Peter Cordes
    Peter Cordes over 6 years
    I made an edit to avoid assuming int is more than 16 bits wide. It does still of course assume that float is 4-byte IEEE754 binary32. A C++11 static_assert or maybe a macro #ifdef / #error could check that. (But of course if C++11 is available, you should use std::round, or for the current rounding mode use std::rint which inlines nicely with gcc and clang).
  • Peter Cordes
    Peter Cordes over 6 years
    This is hilariously inefficient, and also it truncates (by always discarding trailing digits) instead of rounding to nearest.
  • Peter Cordes
    Peter Cordes over 6 years
    BTW, gcc -ffast-math -msse4.1 inlines std::round() to an add( AND(x, L1), OR(x,L2), and then a roundsd. i.e. it fairly efficiently implements round in terms of rint. But there's no reason to do this manually in C++ source, because if you have std::rint() or std::nearbyint() you also have std::round(). See my answer for a godbolt link and a rundown of what inlines or not with different gcc/clang versions.
  • Peter Cordes
    Peter Cordes over 6 years
    TODO: ICC and MSVC are also available on Godbolt, but I haven't looked at their output for this. edits welcome... Also: would it be more useful to break down by compiler / version first and then by function within that? Most people aren't going to switch compilers based on how well they compile FP->FP or FP->integer rounding.
  • jaggedSpire
    jaggedSpire over 6 years
    "I'm not sure why so many answers involve defines, functions, or methods." Have a look at when it was asked--C++11 wasn't out yet. ;)
  • njuffa
    njuffa over 6 years
    @PeterCordes I am well aware how to implement round() efficiently in terms of rint() (when the latter is operating in mode round-to-nearest-or-even): I implemented that for the CUDA standard math library. However, this question seemed to ask how to implement round() with C++ prior to C++11, so rint() would not be available either, only floor() and ceil().
  • njuffa
    njuffa over 6 years
    +1 for recommending rint() where that is a feasible choice, which is usually the case. I guess the name round() implies to some programmers that this is what they want, while rint() seems mysterious. Note that round() doesn't use a "funky" rounding mode: round-to-nearest-ties-away is an official IEEE-754 (2008) rounding mode. It's curious that nearbyint() doesn't get inlined, given that it is largely the same as rint(), and should be identical under -ffast-math conditions. That looks bug-ish to me.
  • njuffa
    njuffa over 6 years
    @PeterCordes Sorry, I misspoke. round() is easily synthesized from rint() in round-to-zero mode, a.k.a. trunc(). Shouldn't have responded before first coffee.
  • Peter Cordes
    Peter Cordes over 6 years
    I was mostly commenting for other readers, and for shameless self-promotion of my answer because I think people are taking this question far too literally, and ignoring the "I need a simple floating point rounding function". The fact that the OP is hoping there is one called "round()" doesn't (to me) indicate that they specifically want the rounding behaviour of std::round() as opposed to the default / current rounding mode if that's more efficient.
  • njuffa
    njuffa over 6 years
    @PeterCordes I agree that it is likely that OP doesn't need the specific rounding behavior of round(); most programmers simply aren't aware of the distinction between round() vs rint() with round-to-nearest-even, where the latter is usually provided directly by the hardware and therefore more efficient; I spelled that out in CUDA Programming Guide to make programmers aware: "The recommended way to round a single-precision floating-point operand to an integer, with the result being a single-precision floating-point number is rintf(), not roundf()".
  • Aleksey F.
    Aleksey F. over 6 years
    @PeterCordes Modern optimizations are welcome. However I was not able to use SSE4.1 as it did not exist at that moment. My purpose was to provide the minimal implementation of round which could function even on old Intel P3 or P4 families from 2000's.
  • Peter Cordes
    Peter Cordes over 6 years
    P3 doesn't even have SSE2, so the compiler will already be using x87 for double, and thus should be able to emit frndint itself for rint(). If your compiler is using SSE2, bouncing a double from an XMM register to x87 and back may not be worth it.
  • n. m.
    n. m. over 6 years
    @GustavoMaciel I know I'm a bit late to the game, but boost implementation is floor(value + 0.5).
  • Gustavo Maciel
    Gustavo Maciel over 6 years
    It actually does not: github.com/boostorg/math/blob/develop/include/boost/math/… 4 years later, I'd also like to say that floor(value + 0.5) is not naive at all, but rather depend on the context and nature of values you want to round!
  • Douglas Daseeco
    Douglas Daseeco about 6 years
    @jaggedSpire, well give me a thumbs up then, if you feel it is appropriate, because all the high scoring answers are obsolete and misleading in the context of today's most commonly used compilers.
  • Shafik Yaghmour
    Shafik Yaghmour about 6 years
  • eri0o
    eri0o over 3 years
    But how is it linked?