251

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??

Post Self
  • 1,471
  • 2
  • 14
  • 34
Roddy
  • 66,617
  • 42
  • 165
  • 277
  • 2
    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. – Frank Feb 17 '11 at 18:05
  • 52
    Protecting this... New users with brilliant new rounding schemes should read existing answers first. – Shog9 Feb 18 '11 at 20:33
  • 13
    `round` is available since C++11 in ``. Unfortunately if you are in Microsoft Visual Studio it is still missing: https://connect.microsoft.com/VisualStudio/feedback/details/775474/missing-round-functions-in-standard-library – Alessandro Jacopson Apr 05 '13 at 08:29
  • 3
    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 Jun 23 '14 at 13:39
  • 3
    @uvts_cvs this does not seem to be an issue with the latest version of visual studio, [see it live](http://rextester.com/ZZLJ88784). – Shafik Yaghmour Jun 23 '14 at 13:49
  • @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 :-( – Alessandro Jacopson Jun 24 '14 at 19:29
  • for basic usage, simply `static_cast(std::round(0.1))`, for more details there are the answers – simplename Apr 06 '22 at 02:48

23 Answers23

150

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.

SuperStormer
  • 4,997
  • 5
  • 25
  • 35
Andreas Magnusson
  • 7,321
  • 3
  • 31
  • 36
  • 54
    This doesn't handle negative numbers correctly. The answer by litb is correct. – Registered User May 22 '09 at 22:02
  • 40
    @InnerJoin: Yes, it handles negative numbers differently to litb's answer, but that doesn't make it "incorrect". – Roddy Jun 10 '09 at 19:59
  • What if you want to round to some places after the decimal precision? multiply and divide? – Lazer Jun 04 '10 at 11:56
  • @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... – Andreas Magnusson Jun 11 '10 at 10:42
  • 41
    Adding 0.5 before truncating fails to round to the nearest integer for several inputs including 0.49999999999999994. See http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1 – Pascal Cuoq May 04 '13 at 18:23
  • 6
    @Roddy no, the fact that it rounds negative numbers incorrect makes it incorrect. – Sergi0 Nov 20 '13 at 13:30
  • 11
    @Sergi0: There is no "correct" and "incorrect" because there are [more than one definitions of rounding](http://en.wikipedia.org/wiki/Rounding#Tie-breaking) that decide what happens at the halfway point. Check your facts before passing judgement. – Jon Nov 25 '13 at 08:31
  • 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 http://en.cppreference.com/w/cpp/types/numeric_limits/round_style and http://en.cppreference.com/w/cpp/numeric/fenv/feround – Muhammad Jan 08 '14 at 18:30
  • 1
    Very important bug since it doesn't use `ceil` if you do `round(-2.5)` it will give back `-2` instead of `-3`. – SSpoke Jan 12 '14 at 22:44
  • 6
    @SSpoke: It rounds up to the nearest larger integer. -2 > -3. It's called "Round Half Up": http://en.wikipedia.org/wiki/Rounding#Round_half_up – Andreas Magnusson Jan 24 '14 at 13:08
  • 21
    @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. – Andreas Magnusson Feb 12 '14 at 13:40
  • 4
    @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 Jun 25 '14 at 13:58
  • @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. – Aaron3468 Sep 02 '16 at 02:03
  • 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 https://stackoverflow.com/questions/485525/round-for-float-in-c/42038116#42038116. – Douglas Daseeco Jun 21 '17 at 12:16
99

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.

artless noise
  • 21,212
  • 6
  • 68
  • 105
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 5
    @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. – Shafik Yaghmour Jul 27 '14 at 10:45
  • 1
    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 Aug 03 '15 at 19:23
  • 3
    @chux interesting, and IEEE 754-2008 standard does specify that rounding preserves signs of zeros and infinities (see 5.9). – Ruslan Mar 08 '16 at 14:23
  • 1
    @Shafik this is a great answer. I've never thought that even rounding is a non-trivial operation. – Ruslan Mar 08 '16 at 14:24
  • 1
    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` https://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 Nov 17 '17 at 03:50
  • Also see [this all integer implementation](https://godbolt.org/g/7EBt7n) via [this tweet](https://twitter.com/stephentyrone/status/989322897100591105) – Shafik Yaghmour Apr 26 '18 at 02:11
96

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.

Daniel Wolf
  • 12,855
  • 13
  • 54
  • 80
  • 2
    I was already using boost in my project, +1 for this, much better than using the naïve `floor(value + 0.5)` approach! – Gustavo Maciel Jun 12 '14 at 00:51
  • @GustavoMaciel I know I'm a bit late to the game, but boost implementation is `floor(value + 0.5)`. – n. m. could be an AI Jan 18 '18 at 18:22
  • It actually does not: https://github.com/boostorg/math/blob/develop/include/boost/math/special_functions/round.hpp 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! – Gustavo Maciel Jan 25 '18 at 03:14
72

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); 
}
kalaxy
  • 1,608
  • 1
  • 14
  • 14
  • 3
    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 Nov 27 '13 at 12:14
  • @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. – kalaxy Nov 27 '13 at 21:08
  • see aka.nice's answer and the links provided - try with 5000000000000001.0 for example – stijn Nov 28 '13 at 07:43
  • 1
    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. – stijn Nov 28 '13 at 12:59
  • 1
    @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. – Pascal Cuoq Nov 28 '13 at 23:29
  • The resulting value should be floating point value with double precision. Its not an answer for question asked. – truthseeker Jan 03 '15 at 12:24
  • 3
    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. – Tony Delroy Jul 21 '15 at 10:10
50

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
schibum
  • 794
  • 7
  • 7
27

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.

MSN
  • 53,214
  • 7
  • 75
  • 105
  • 1
    It's good to make the distinction between different versions of 'round'. It's good to know when to pick which, too. – xtofl Jan 27 '09 at 22:17
  • 7
    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: http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1 – Bruce Dawson Jun 23 '16 at 20:43
14

There are 2 problems we are looking at:

  1. rounding conversions
  2. type conversion.

Rounding conversions mean rounding ± float/double to nearest floor/ceil float/double. May be your problem ends here. But if you are expected to return Int/Long, you need to perform type conversion, and thus "Overflow" problem might hit your solution. SO, do a check for error in your function

long round(double x) {
   assert(x >= LONG_MIN-0.5);
   assert(x <= LONG_MAX+0.5);
   if (x >= 0)
      return (long) (x+0.5);
   return (long) (x-0.5);
}

#define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
      error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

from : http://www.cs.tut.fi/~jkorpela/round.html

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
Sangeet
  • 422
  • 3
  • 5
  • 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. – chux - Reinstate Monica Aug 30 '16 at 20:48
  • 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)`](http://en.cppreference.com/w/cpp/numeric/math/rint) if it's available. – Peter Cordes Nov 17 '17 at 03:54
11

A certain type of rounding is also implemented in Boost:

#include <iostream>

#include <boost/numeric/conversion/converter.hpp>

template<typename T, typename S> T round2(const S& x) {
  typedef boost::numeric::conversion_traits<T, S> Traits;
  typedef boost::numeric::def_overflow_handler OverflowHandler;
  typedef boost::numeric::RoundEven<typename Traits::source_type> Rounder;
  typedef boost::numeric::converter<T, S, Traits, OverflowHandler, Rounder> Converter;
  return Converter::convert(x);
}

int main() {
  std::cout << round2<int, double>(0.1) << ' ' << round2<int, double>(-0.1) << ' ' << round2<int, double>(-0.9) << std::endl;
}

Note that this works only if you do a to-integer conversion.

Philipp
  • 48,066
  • 12
  • 84
  • 109
  • 2
    Boost also offers a set of simple rounding functions; see my answer. – Daniel Wolf May 01 '11 at 16:21
  • 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 – stijn Nov 27 '13 at 13:14
7

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;
}
Carl
  • 43,122
  • 10
  • 80
  • 104
  • 4
    Unless your compiler int size defaults to 1024 bits, this ain't gonna be accurate for huge double... – aka.nice Jun 15 '12 at 13:27
  • 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. – Carl Jun 15 '12 at 20:48
  • 3
    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 Jun 17 '12 at 19:59
  • 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`](http://felixcloutier.com/x86/CVTTSD2SI.html) as the integer bit pattern, i.e. `INT_MIN`, which will then be converted back to `double`. – Peter Cordes Nov 17 '17 at 03:57
7

These days it shouldn't be a problem to use a C++11 compiler which includes a C99/C++11 math library. But then the question becomes: which rounding function do you pick?

C99/C++11 round() is often not actually the rounding function you want. It uses a funky rounding mode that rounds away from 0 as a tie-break on half-way cases (+-xxx.5000). If you do specifically want that rounding mode, or you're targeting a C++ implementation where round() is faster than rint(), then use it (or emulate its behaviour with one of the other answers on this question which took it at face value and carefully reproduced that specific rounding behaviour.)

round()'s rounding is different from the IEEE754 default round to nearest mode with even as a tie-break. Nearest-even avoids statistical bias in the average magnitude of numbers, but does bias towards even numbers.

There are two math library rounding functions that use the current default rounding mode: std::nearbyint() and std::rint(), both added in C99/C++11, so they're available any time std::round() is. The only difference is that nearbyint never raises FE_INEXACT.

Prefer rint() for performance reasons: gcc and clang both inline it more easily, but gcc never inlines nearbyint() (even with -ffast-math)


gcc/clang for x86-64 and AArch64

I put some test functions on Matt Godbolt's Compiler Explorer, where you can see source + asm output (for multiple compilers). For more about reading compiler output, see this Q&A, and Matt's CppCon2017 talk: “What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid”,

In FP code, it's usually a big win to inline small functions. Especially on non-Windows, where the standard calling convention has no call-preserved registers, so the compiler can't keep any FP values in XMM registers across a call. So even if you don't really know asm, you can still easily see whether it's just a tail-call to the library function or whether it inlined to one or two math instructions. Anything that inlines to one or two instructions is better than a function call (for this particular task on x86 or ARM).

On x86, anything that inlines to SSE4.1 roundsd can auto-vectorize with SSE4.1 roundpd (or AVX vroundpd). (FP->integer conversions are also available in packed SIMD form, except for FP->64-bit integer which requires AVX512.)

  • std::nearbyint():

    • x86 clang: inlines to a single insn with -msse4.1.
    • x86 gcc: inlines to a single insn only with -msse4.1 -ffast-math, and only on gcc 5.4 and earlier. Later gcc never inlines it (maybe they didn't realize that one of the immediate bits can suppress the inexact exception? That's what clang uses, but older gcc uses the same immediate as for rint when it does inline it)
    • AArch64 gcc6.3: inlines to a single insn by default.
  • std::rint:

    • x86 clang: inlines to a single insn with -msse4.1
    • x86 gcc7: inlines to a single insn with -msse4.1. (Without SSE4.1, inlines to several instructions)
    • x86 gcc6.x and earlier: inlines to a single insn with -ffast-math -msse4.1.
    • AArch64 gcc: inlines to a single insn by default
  • std::round:

    • x86 clang: doesn't inline
    • x86 gcc: inlines to multiple instructions with -ffast-math -msse4.1, requiring two vector constants.
    • AArch64 gcc: inlines to a single instruction (HW support for this rounding mode as well as IEEE default and most others.)
  • std::floor / std::ceil / std::trunc

    • x86 clang: inlines to a single insn with -msse4.1
    • x86 gcc7.x: inlines to a single insn with -msse4.1
    • x86 gcc6.x and earlier: inlines to a single insn with -ffast-math -msse4.1
    • AArch64 gcc: inlines by default to a single instruction

Rounding to int / long / long long:

You have two options here: use lrint (like rint but returns long, or long long for llrint), or use an FP->FP rounding function and then convert to an integer type the normal way (with truncation). Some compilers optimize one way better than the other.

long l = lrint(x);

int  i = (int)rint(x);

Note that int i = lrint(x) converts float or double -> long first, and then truncates the integer to int. This makes a difference for out-of-range integers: Undefined Behaviour in C++, but well-defined for the x86 FP -> int instructions (which the compiler will emit unless it sees the UB at compile time while doing constant propagation, then it's allowed to make code that breaks if it's ever executed).

On x86, an FP->integer conversion that overflows the integer produces INT_MIN or LLONG_MIN (a bit-pattern of 0x8000000 or the 64-bit equivalent, with just the sign-bit set). Intel calls this the "integer indefinite" value. (See the cvttsd2si manual entry, the SSE2 instruction that converts (with truncation) scalar double to signed integer. It's available with 32-bit or 64-bit integer destination (in 64-bit mode only). There's also a cvtsd2si (convert with current rounding mode), which is what we'd like the compiler to emit, but unfortunately gcc and clang won't do that without -ffast-math.

Also beware that FP to/from unsigned int / long is less efficient on x86 (without AVX512). Conversion to 32-bit unsigned on a 64-bit machine is pretty cheap; just convert to 64-bit signed and truncate. But otherwise it's significantly slower.

  • x86 clang with/without -ffast-math -msse4.1: (int/long)rint inlines to roundsd / cvttsd2si. (missed optimization to cvtsd2si). lrint doesn't inline at all.

  • x86 gcc6.x and earlier without -ffast-math: neither way inlines

  • x86 gcc7 without -ffast-math: (int/long)rint rounds and converts separately (with 2 total instructions of SSE4.1 is enabled, otherwise with a bunch of code inlined for rint without roundsd). lrint doesn't inline.
  • x86 gcc with -ffast-math: all ways inline to cvtsd2si (optimal), no need for SSE4.1.

  • AArch64 gcc6.3 without -ffast-math: (int/long)rint inlines to 2 instructions. lrint doesn't inline

  • AArch64 gcc6.3 with -ffast-math: (int/long)rint compiles to a call to lrint. lrint doesn't inline. This may be a missed optimization unless the two instructions we get without -ffast-math are very slow.
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 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. – Peter Cordes Nov 17 '17 at 09:32
  • 2
    +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 Nov 17 '17 at 17:01
5

If you ultimately want to convert the double output of your round() function to an int, then the accepted solutions of this question will look something like:

int roundint(double r) {
  return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}

This clocks in at around 8.88 ns on my machine when passed in uniformly random values.

The below is functionally equivalent, as far as I can tell, but clocks in at 2.48 ns on my machine, for a significant performance advantage:

int roundint (double r) {
  int tmp = static_cast<int> (r);
  tmp += (r-tmp>=.5) - (r-tmp<=-.5);
  return tmp;
}

Among the reasons for the better performance is the skipped branching.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dshin
  • 2,354
  • 19
  • 29
  • 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`](http://felixcloutier.com/x86/CVTTSD2SI.html) as the integer bit pattern, i.e. `INT_MIN`, which will then be converted back to `double`. – Peter Cordes Nov 17 '17 at 03:58
4

Beware of floor(x+0.5). Here is what can happen for odd numbers in range [2^52,2^53]:

-bash-3.2$ cat >test-round.c <<END

#include <math.h>
#include <stdio.h>

int main() {
    double x=5000000000000001.0;
    double y=round(x);
    double z=floor(x+0.5);
    printf("      x     =%f\n",x);
    printf("round(x)    =%f\n",y);
    printf("floor(x+0.5)=%f\n",z);
    return 0;
}
END

-bash-3.2$ gcc test-round.c
-bash-3.2$ ./a.out
      x     =5000000000000001.000000
round(x)    =5000000000000001.000000
floor(x+0.5)=5000000000000002.000000

This is http://bugs.squeak.org/view.php?id=7134. Use a solution like the one of @konik.

My own robust version would be something like:

double round(double x)
{
    double truncated,roundedFraction;
    double fraction = modf(x, &truncated);
    modf(2.0*fraction, &roundedFraction);
    return truncated + roundedFraction;
}

Another reason to avoid floor(x+0.5) is given here.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
aka.nice
  • 9,100
  • 1
  • 28
  • 40
  • 2
    I'm interested to know about the downvotes. Is it because the tie is resolved away from zero rather than to nearest even? – aka.nice Jul 27 '14 at 13:05
  • 1
    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. – chux - Reinstate Monica Aug 03 '15 at 19:25
2

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.

Douglas Daseeco
  • 3,475
  • 21
  • 27
  • 1
    "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. ;) – jaggedSpire Nov 17 '17 at 16:10
  • @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. – Douglas Daseeco Mar 10 '18 at 12:35
2

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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
konik
  • 37
  • 1
  • 2
    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 Jun 17 '12 at 20:18
  • 2
    I checked in ISO/IEC 10967-2 standard, http://www.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))) – aka.nice Jul 27 '12 at 12:59
  • 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 Nov 17 '17 at 04:01
1

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);
    }
Jarod42
  • 203,559
  • 14
  • 181
  • 302
quant
  • 21,507
  • 32
  • 115
  • 211
  • 1
    As I pointed out in [my answer](http://stackoverflow.com/a/24348037/1708801) 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 Jul 08 '14 at 00:59
1

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.

1

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;
}
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
njuffa
  • 23,970
  • 4
  • 78
  • 130
  • 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 Nov 17 '17 at 04:28
  • 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 Nov 17 '17 at 09:31
  • @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 Nov 17 '17 at 16:55
  • @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. – njuffa Nov 17 '17 at 17:48
  • 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. – Peter Cordes Nov 17 '17 at 17:55
  • 1
    @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()`". – njuffa Nov 17 '17 at 18:32
0

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
Aleksey F.
  • 751
  • 7
  • 19
  • Result value should be floating point value with double precision. – truthseeker Jan 03 '15 at 12:18
  • @ truthseeker: Yeah, I had to see the required type of return value. OK, see "UPD". – Aleksey F. Jan 04 '15 at 15:21
  • 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 Nov 17 '17 at 04:07
  • @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. – Aleksey F. Nov 19 '17 at 12:50
  • 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. – Peter Cordes Nov 19 '17 at 15:07
0

Since C++ 11 simply:

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

or to get int

static_cast<int>(std::round(1.1))
hgrey
  • 3,033
  • 17
  • 21
0

round_f for ARM with math

static inline float round_f(float value)
{
    float rep;
    asm volatile ("vrinta.f32 %0,%1" : "=t"(rep) : "t"(value));
    return rep;
}

round_f for ARM without math

union f__raw {
    struct {
        uint32_t massa  :23;
        uint32_t order  :8;
        uint32_t sign   :1;
    };
    int32_t     i_raw;
    float       f_raw;
};

float round_f(float value)
{
    union f__raw raw;
    int32_t exx;
    uint32_t ex_mask;
    raw.f_raw = value;
    exx = raw.order - 126;
    if (exx < 0) {
        raw.i_raw &= 0x80000000;
    } else if (exx < 24) {
        ex_mask = 0x00ffffff >> exx;
        raw.i_raw += 0x00800000 >> exx;
        if (exx == 0) ex_mask >>= 1;
        raw.i_raw &= ~ex_mask;
    };
    return  raw.f_raw;
};
-1

Best way to rounding off a floating value by "n" decimal places, is as following with in O(1) time:-

We have to round off the value by 3 places i.e. n=3.So,

float a=47.8732355;
printf("%.3f",a);
Imran
  • 5,542
  • 3
  • 23
  • 46
Parveen Kumar
  • 175
  • 3
  • 5
-4
// Convert the float to a string
// We might use stringstream, but it looks like it truncates the float to only
//5 decimal points (maybe that's what you want anyway =P)

float MyFloat = 5.11133333311111333;
float NewConvertedFloat = 0.0;
string FirstString = " ";
string SecondString = " ";
stringstream ss (stringstream::in | stringstream::out);
ss << MyFloat;
FirstString = ss.str();

// Take out how ever many decimal places you want
// (this is a string it includes the point)
SecondString = FirstString.substr(0,5);
//whatever precision decimal place you want

// Convert it back to a float
stringstream(SecondString) >> NewConvertedFloat;
cout << NewConvertedFloat;
system("pause");

It might be an inefficient dirty way of conversion but heck, it works lol. And it's good, because it applies to the actual float. Not just affecting the output visually.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Brad
  • 11
  • 1
  • 1
    This is *hilariously* inefficient, and also it truncates (by always discarding trailing digits) instead of rounding to nearest. – Peter Cordes Nov 17 '17 at 04:30
-6

I did this:

#include <cmath.h>

using namespace std;

double roundh(double number, int place){

    /* place = decimal point. Putting in 0 will make it round to whole
                              number. putting in 1 will round to the
                              tenths digit.
    */

    number *= 10^place;
    int istack = (int)floor(number);
    int out = number-istack;
    if (out < 0.5){
        floor(number);
        number /= 10^place;
        return number;
    }
    if (out > 0.4) {
        ceil(number);
        number /= 10^place;
        return number;
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 3
    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. – Pete855217 Aug 10 '12 at 08:48