views:

112

answers:

7

Hello,

I want to convert a floating point value to its integer representation. As this will be used in comparisons, the default rounding mode (round_to_nearest) is not appropriate for me. As far as I know I can't specify the rounding mode to the FPU in a C++ standard compliant way (not even in C++0x). What are the other ways to accomplish that and which one of them is the most portable? I'm programming on Linux, GCC, i386 + x86-64.

Thanks!

EDIT

I'm interested in round_toward_infinity and round_toward_neg_infinity rounding modes and I want to be able select one of them each time.

+1  A: 

You have to specify which type of rounding you do want.

Think about it in these terms:

  1. take a floating point variable

  2. round it appropriately

  3. cast to integer will get the integer part of the floating point value - it truncates the value

You might look at floor() and ceil() for round-down and round-up respectively.

You have to pay particular attention to what you want to happen around 0 and negative numbers.

GLIBC has lots of rounding functions available.

Here's a round-towards-infinity function:

int round_towards_infinity(double value) {
  if(value > 0)
     return ceil(value);
  return floor(value);
}
Will
these functions do **NOT** convert float to int. I will still get the result of conversion rounded in a way I don't want. That's the problem.
buratinas
@buratinas, if you convert a ceil()ed value to int, you get the rounded-up value. Its a question of the order that you apply these functions to your terms.
Will
@buratinas But we still don't know what rounding you want, please edit your question
CharlesB
[assume round_towards_infinity in the following example] so if I have double x = *****12.3000 and the next representable value of it is: nextafterf(x) = ******14.7000, what will the ceil(x) do? even if it changes the value it is not the behavior I want (as I'll get (int)x = *****14 while the correct answer is (int)x=****13)
buratinas
@buratinas what is nextafterf() and why is it not explained in the question? your question is hard to answer because it is poorly stated.
Will
Will, `ceil` always rounds towards infinity, even for negative numbers.
avakar
nextafterf is C99 standard function which returns the next representable value for a floating pint number
buratinas
@avakar http://www.cplusplus.com/reference/clibrary/cmath/ceil/ shows negative numbers being rounded towards positive infinity. I meant the function to round negative numbers towards negative infinity, as that's something like I interpret the question (but we are all confused by the question, so ...)
Will
Will, right, I interpreted "round to infinity" as "round to positive infinity".
avakar
+1  A: 

You should use floor and ceil. The prototype is not int floor nor int ceil, but they do return an integer value.

#include <math.h>
#include <stdio.h>

int main()
{
        float x = 6.04;
        printf("floor = %f ceil = %f\n", floor(x), ceil(x));
        return 0;
}

Produces:

$ ./test 
floor = 6.000000 ceil = 7.000000

So you just now have to cast the answer.

Aif
this function does **NOT** convert float to int. I will still get the result of conversion rounded in a way I don't want. That's the problem.
buratinas
I think I see what you mean, but I don't know for now :)
Aif
floor and ceil return floating point types but you get whole integers. If you want to round towards +- infinity then use floor for negative numbers and ceil for positive numbers.
CashCow
err. why it isn't possible to downvote comments?
buratinas
A: 

Maybe like this :

#include <cmath>

int Round( const double a )
{
  return static_cast< int >( std::floor( a + 0.5 ) );
}
VJo
no, it's not usable in this problem (see edited question)
buratinas
A: 

The conversion I've always been taught to use (although I'm sure proper floating point mathematicians will shoot me for it) has been:

float x = ....;
int   y = int(x + 0.5f);

A quick look through my reference says that C99 has an "lrint" family of functions that take floating point values and return long ints. It may be what you are looking for.

Kaz Dragon
If you want correct rounding for negative numbers then you need to take the sign into account, e.g. `y = (int)(x >= 0.0 ? x + 0.5 : x - 0.5);`
Paul R
@Paul R - good point.
Kaz Dragon
no, this is not usable in this problem (see edited question)
buratinas
Yes, you are correct. This is totally not a solution for the additional constraints you added since I attempted to answer your question. Query: Are the numbers at the scale you talk of even representable in an int?
Kaz Dragon
yes, they are: the difference between two consecutive representable values is more than 1.0000 : for float x when x > ~1.6e7 (max value of int is ~2e9); for double y when y > ~9e15 (max value of long is ~9.2e18)
buratinas
@buratinas: So, the ints are representable for floats, but not for doubles?
Kaz Dragon
A: 

If both 12.3 and 14.7 are floats then in IEEE 754 both 13.0 and 14.0 are floats (more generally: given two non-integer floats, all integers in between are floats as well). Therefore, under IEEE 754, floor and ceil always work correctly (note that x86 uses IEEE 754 representations).

If you're not under IEEE 754, note that the definition of floor says (C99:7.12.9.2/3, I don't have C90 handy)

The floor functions return the largest integer value not greater than x, expressed as a floating-point number.

I don't know how to interpret that if the largest integer value not greater than x cannot be represented exactly.

avakar
I was talking about e.g. 1235676812.3 and 1235676814.7 i.e. big numbers. This is very clear from the edited question
buratinas
@buratinas, if 1235676812.3 and 1235676814.7 are floats, then both 1235676813 and 1235676814 are floats. This holds for any two floats, regardless of how large they are. Read my answer more carefully.
avakar
+2  A: 

Since C99 there's llrint() which rounds to the next (long long) integer, using the current rounding mode (that can be set by fesetround()).

groovingandi
A: 

Sounds like you're going to have to do some bit twiddling to get the right answer. Extract the exponent of the float and use that to shift the bit pattern of the mantissa by the right amount. You may need to do some additional trickery to handle +/- infinity and overflows and denormalised values... but it sounds like a painful path.. I'd not walk it if you really didn't have to.

Michael Anderson