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??
I use the following implementation of round in asm for x86 architecture and MS VS specific C++:
UPD: to return double value
Output:
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:
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.
It's available since C++11 in cmath (according to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf)
Output:
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 whereround()
is faster thanrint()
, 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()
andstd::rint()
, both added in C99/C++11, so they're available any timestd::round()
is. The only difference is thatnearbyint
never raises FE_INEXACT.Prefer
rint()
for performance reasons: gcc and clang both inline it more easily, but gcc never inlinesnearbyint()
(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.1roundpd
(or AVXvroundpd
). (FP->integer conversions are also available in packed SIMD form, except for FP->64-bit integer which requires AVX512.)std::nearbyint()
:-msse4.1
.-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 forrint
when it does inline it)std::rint
:-msse4.1
-msse4.1
. (Without SSE4.1, inlines to several instructions)-ffast-math -msse4.1
.std::round
:-ffast-math -msse4.1
, requiring two vector constants.std::floor
/std::ceil
/std::trunc
-msse4.1
-msse4.1
-ffast-math -msse4.1
Rounding to
int
/long
/long long
:You have two options here: use
lrint
(likerint
but returnslong
, orlong long
forllrint
), 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.Note that
int i = lrint(x)
convertsfloat
ordouble
->long
first, and then truncates the integer toint
. 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
orLLONG_MIN
(a bit-pattern of0x8000000
or the 64-bit equivalent, with just the sign-bit set). Intel calls this the "integer indefinite" value. (See thecvttsd2si
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 acvtsd2si
(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 toroundsd
/cvttsd2si
. (missed optimization tocvtsd2si
).lrint
doesn't inline at all.x86 gcc6.x and earlier without
-ffast-math
: neither way inlines-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 forrint
withoutroundsd
).lrint
doesn't inline.x86 gcc with
-ffast-math
: all ways inline tocvtsd2si
(optimal), no need for SSE4.1.AArch64 gcc6.3 without
-ffast-math
:(int/long)rint
inlines to 2 instructions.lrint
doesn't inline-ffast-math
:(int/long)rint
compiles to a call tolrint
.lrint
doesn't inline. This may be a missed optimization unless the two instructions we get without-ffast-math
are very slow.Function
double round(double)
with the use of themodf
function:To be compile clean, includes "math.h" and "limits" are necessary. The function works according to a following rounding schema:
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.
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).
In C++11
We have the following and additional overloads in #include <cmath> that rely on IEEE double precision floating point.
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.
With Ordinal Division
When dividing two ordinal numbers, where T is short, int, long, or another ordinal, the rounding expression is this.
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.