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 );
}