round() for float in C++
Asked Answered
M

23

255

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

Moonlight answered 27/1, 2009 at 22:6 Comment(7)
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.Ruffo
Protecting this... New users with brilliant new rounding schemes should read existing answers first.Lunula
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/…Racy
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.Logic
@uvts_cvs this does not seem to be an issue with the latest version of visual studio, see it live.Logic
@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 :-(Racy
for basic usage, simply static_cast<int>(std::round(0.1)), for more details there are the answersLancers
F
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
Fatherinlaw answered 19/6, 2012 at 13:23 Comment(3)
there is also lround and llround for integral resultsYarvis
@sp2danny: or better, lrint to use the current rounding mode instead of round's funky away-from-zero tiebreak.Banlieue
But how is it linked?Stinnett
D
149

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.

Daggerboard answered 27/1, 2009 at 22:10 Comment(14)
This doesn't handle negative numbers correctly. The answer by litb is correct.Seema
@InnerJoin: Yes, it handles negative numbers differently to litb's answer, but that doesn't make it "incorrect".Moonlight
What if you want to round to some places after the decimal precision? multiply and divide?Ultraviolet
@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...Daggerboard
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/nearbyintf1Chalutz
@Moonlight no, the fact that it rounds negative numbers incorrect makes it incorrect.Voodooism
@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.Jeweller
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/feroundBramble
Very important bug since it doesn't use ceil if you do round(-2.5) it will give back -2 instead of -3.Impartial
@SSpoke: It rounds up to the nearest larger integer. -2 > -3. It's called "Round Half Up": en.wikipedia.org/wiki/Rounding#Round_half_upDaggerboard
@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.Daggerboard
@Moonlight 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.Logic
@Jeweller 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.Pipage
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://mcmap.net/q/37510/-round-for-float-in-c/….Landgrave
L
102

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.

Logic answered 22/6, 2014 at 4:7 Comment(6)
@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.Logic
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.Stalinsk
@chux interesting, and IEEE 754-2008 standard does specify that rounding preserves signs of zeros and infinities (see 5.9).Nematic
@Shafik this is a great answer. I've never thought that even rounding is a non-trivial operation.Nematic
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-mathBanlieue
Also see this all integer implementation via this tweetLogic
S
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.

Shipworm answered 1/5, 2011 at 16:19 Comment(3)
I was already using boost in my project, +1 for this, much better than using the naïve floor(value + 0.5) approach!Pacific
@GustavoMaciel I know I'm a bit late to the game, but boost implementation is floor(value + 0.5).Outlander
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!Pacific
K
73

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); 
}
Konstance answered 11/1, 2011 at 16:45 Comment(7)
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)Cockboat
@Cockboat 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.Konstance
see aka.nice's answer and the links provided - try with 5000000000000001.0 for exampleCockboat
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.Cockboat
@Cockboat 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.Chalutz
The resulting value should be floating point value with double precision. Its not an answer for question asked.Andres
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.Leith
F
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
Fatherinlaw answered 19/6, 2012 at 13:23 Comment(3)
there is also lround and llround for integral resultsYarvis
@sp2danny: or better, lrint to use the current rounding mode instead of round's funky away-from-zero tiebreak.Banlieue
But how is it linked?Stinnett
W
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.

Wot answered 27/1, 2009 at 22:8 Comment(2)
It's good to make the distinction between different versions of 'round'. It's good to know when to pick which, too.Pyles
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/nearbyintf1Yong
B
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

Beffrey answered 29/5, 2010 at 8:49 Comment(2)
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.Stalinsk
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.Banlieue
P
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.

Pepsin answered 29/6, 2010 at 13:2 Comment(2)
Boost also offers a set of simple rounding functions; see my answer.Shipworm
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.niceCockboat
S
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;
}
Subtend answered 12/2, 2010 at 3:2 Comment(4)
Unless your compiler int size defaults to 1024 bits, this ain't gonna be accurate for huge double...Compagnie
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.Subtend
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.Compagnie
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.Banlieue
B
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.
Banlieue answered 17/11, 2017 at 9:27 Comment(2)
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.Banlieue
+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.Adulation
L
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.

Lukasz answered 25/4, 2013 at 21:2 Comment(1)
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.Banlieue
C
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.

Compagnie answered 17/6, 2012 at 20:50 Comment(2)
I'm interested to know about the downvotes. Is it because the tie is resolved away from zero rather than to nearest even?Compagnie
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.Stalinsk
P
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
Pseudoscope answered 9/2, 2011 at 11:35 Comment(3)
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.Compagnie
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)))Compagnie
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...Banlieue
L
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.

Landgrave answered 4/2, 2017 at 8:16 Comment(2)
"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. ;)Prehistory
@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.Landgrave
S
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);
    }
Suomi answered 24/10, 2013 at 0:3 Comment(1)
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.Logic
S
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.

Supraliminal answered 16/12, 2015 at 0:59 Comment(0)
A
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;
}
Adulation answered 16/11, 2017 at 11:57 Comment(6)
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).Banlieue
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.Banlieue
@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().Adulation
@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.Adulation
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.Banlieue
@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()".Adulation
S
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
Sooty answered 28/10, 2014 at 10:32 Comment(5)
Result value should be floating point value with double precision.Andres
@ truthseeker: Yeah, I had to see the required type of return value. OK, see "UPD".Sooty
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.Banlieue
@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.Sooty
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.Banlieue
T
0

Since C++ 11 simply:

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

or to get int

static_cast<int>(std::round(1.1))
Tabloid answered 19/7, 2020 at 20:28 Comment(0)
C
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;
};
Cuckooflower answered 13/1, 2022 at 0:21 Comment(0)
L
-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);
Litter answered 9/8, 2019 at 1:34 Comment(0)
C
-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.

Collett answered 29/6, 2010 at 12:31 Comment(1)
This is hilariously inefficient, and also it truncates (by always discarding trailing digits) instead of rounding to nearest.Banlieue
I
-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;
    }
}
Independence answered 9/5, 2009 at 16:47 Comment(1)
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.Boanerges

© 2022 - 2024 — McMap. All rights reserved.