Concise way to implement round() in C?
You could re-invent the wheel, as many other answers suggest. Alternately, you could use someone else's wheel -- I'd suggest Newlib's, which is BSD-licensed and intended for use on embedded systems. It properly handles negative numbers, NaNs, infinities, and cases which are not representable as integers (due to being too large), as well as doing so in an efficient manner that uses exponents and masking rather than generally-costlier floating-point operations. In addition, it's regularly tested, so you know it doesn't have glaring corner-case bugs in it.
The Newlib source can be a bit awkward to navigate, so here are the bits you want:
Float version: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/sf_round.c;hb=master
Double version: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/s_round.c;hb=master
Word-extraction macros defined here: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/fdlibm.h;hb=master
If you need other files from there, the parent directory is this one: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=tree;f=newlib/libm/common;hb=master
For the record, here's the code for the float version. As you can see, there's a bit of complexity required to deal with all the possible cases correctly.
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;
}
int round(double x)
{
if (x < 0.0)
return (int)(x - 0.5);
else
return (int)(x + 0.5);
}
IEEE 754 recommends the "round half to even" approach: if the fractional part of d
is 0.5 then round to the nearest even integer. The problem is that rounding a fractional part of 0.5 the same direction introduces bias in the results; so, you have to round a fractional 0.5 up half the time and down half the time, hence the "round to the nearest even integer" bit, rounding to the nearest odd would also work as would flipping a fair coin to determine which way to go.
I think something more like this would be IEEE-correct:
#include <math.h>
int is_even(double d) {
double int_part;
modf(d / 2.0, &int_part);
return 2.0 * int_part == d;
}
double round_ieee_754(double d) {
double i = floor(d);
d -= i;
if(d < 0.5)
return i;
if(d > 0.5)
return i + 1.0;
if(is_even(i))
return i;
return i + 1.0;
}
And this one should be C99-ish (which appears to specify that numbers with fractional parts of 0.5 should be rounded away from zero):
#include <math.h>
double round_c99(double x) {
return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}
And a more compact version of my first round_c99()
, this one handles crossing the 56bit mantissa boundary better by not relying on x+0.5
or x-0.5
being sensible things to do:
#include <math.h>
double round_c99(double d) {
double int_part, frac_part;
frac_part = modf(d, &int_part);
if(fabs(frac_part) < 0.5)
return int_part;
return int_part > 0.0 ? int_part + 1.0 : int_part - 1.0;
}
This will have problems if |int_part| >> 1
but rounding a double with a large exponent is pointless. I'm sure there are NaN in all three as well but my masochism has limits and numerical programming really isn't my thing.
Floating point computation has ample room for subtle errors so concise may not be the best requirement.
An even better solution would be to beat your compiler vendor roughly about the face and neck until they provide a proper math library.
int round(float x)
{
return (int)(x + 0.5);
}
Caveat: Only works on positive numbers.