Topic: round a float into an int
Author: jamshid@ses.com (Jamshid Afshar)
Date: Wed, 5 Jan 1994 19:10:56 GMT Raw View
Moving to comp.std.c++ in search of the perfect:
int round(double);
function.
In article <1993Dec20.033706.21985@zcon.com>,
Syed Zaeem Hosain <szh@zcon.com> wrote:
> float f; int i;
>[...]
>The third rounds to nearest integer, assuming that positive and
>negative values are possible: [...]
>P.S. One response I saw to this post that I decided was a little easier
>to read was to change the last example shown above to:
>
> i = (int) ((f >= 0.0) ? f + 0.5 : f - 0.5);
I posted Zyed's article locally as a "C++ tip" but was surprised when
a colleague pointed out that it results in unspecified behavior if `f'
is negative. John Nagle also wrote in article
<nagleCIBLDD.L72@netcom.com> that the direction of truncation is
undefined for negative floating point values in C and C++.
ARM 4.4: "Conversion of a floating value to an integral type truncates
[...] Such conversions are machine dependent; for example, the
direction of truncation of negative numbers varies from machine to
machine." So, if `f' were -0.7, subtracting 0.5 would result in -1.2,
but converting that to int would result in either -1 or -2, depending
on your compiler (and options, etc.).
John's slightly modified code (the test was backwards) is:
float x = /*...*/;
int i;
if (x > 0)
i = int( x + 0.5 );
else
i = -int( -x + 0.5 ); // positve float value converted to int
My only complaint is that this might not work if `x' is INT_MIN
(-INT_MIN might not be representable). So, how about:
if ( x >= 0 ) i = int( x + 0.5 );
else i = int( ceil( f - 0.5 ) );
Is there any way to round any float value between INT_MIN and INT_MAX
without <math.h>? Can `-f' fail in John's code if `f' is negative and
|f| is very large? Is an ANSI C/C++ solution out of the question
since so little about floating point arithmetic is specified? What
does the Numerical C Standard(?) say?
Anyway, I'm appending my round() and a test program. It runs fine
under g++ 2.5.5, BC++ and Watcom but I couldn't get it running under
Centerline/Cfront 3.0.2 (sparc). I'm not sure if the Cfront problem
is with INT_MIN being defined as -0x80000000 and double(INT_MIN)
evaluating to a positive value or with Cfront's ridiculously buggy
floating point conversions. Cfront 3.0.2 outputs "1" for:
cout << int(1.7+0.5) << endl;
The workaround is:
double d = 1.7+0.5;
cout << int(d) << endl;
Jamshid Afshar
jamshid@ses.com
--- test program for Jam's round() ---
#include <limits.h>
#include <math.h>
#include <assert.h>
const int int_min = int(INT_MIN);
const int int_max = int(INT_MAX);
int round(double f) {
assert( f>=int_min && f<=int_max );
if ( f >= 0.0 ) return int(f+0.5);
else return int(ceil(f-0.5)); // unlike -int((-f)+0.5), works with INT_MIN
}
main() {
assert( round(0) == 0 );
assert( round(0.2) == 0 );
assert( round(0.5) == 1 );
assert( round(1.7) == 2 );
assert( round(-1.7) == -2 );
assert( round(-1.2) == -1 );
assert( round(int_min/2) == int_min/2 );
assert( round(int_min+1) == int_min+1 );
assert( round(int_max-1) == int_max-1 );
assert( round(int_min) == int_min );
assert( round(int_max) == int_max );
}