2

Working in C language, I would like to round a float number to its inferior odd integer and its inferior even integer. The speed of the solution is very important (because it is computed 2M*20 times per seconds).

I propose this solution :

x_even = (int)floor(x_f) & ~1;
x_odd  = ((int)ceil(x_f) & ~1) -1;

I presume that the weak point is the floor and ceil operations, but I'm not even sure of that.

Does someone have a comment on this solution ; I'm interested about it's speed of execution, but if you have another solution to share, I'll be very happy to test it :-).

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
Jav
  • 1,445
  • 1
  • 18
  • 47
  • 1
    So round 1.5 to 1 and 0, respectively? -0.5 to -1 and -2, right? –  May 03 '13 at 16:13
  • I think this would make more sense if it weren't called "rounding". It's a reasonably well-defined computation (although, what should be done for NaN and values outside the range `INT_MIN` to `INT_MAX`?) – Ben Voigt May 03 '13 at 16:15
  • 3
    I actually just read this today. Might be interesting. http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1 – BoBTFish May 03 '13 at 16:16
  • Is `x_f` actually a value from an array? Are you on an x86 machine with a reasonably modern processor (< 10 year old)? If so, you could process four floats (or two doubles) in one go with SSE instructions, which may help, and you can do the & and -1 operation using SSE too. – Mats Petersson May 03 '13 at 16:26
  • Do you know anything about the value of the number? If it is in [0, 0x1p24], then you can get x_even by setting the rounding mode to downward and using `x_f + 0x1p24f - 0x1p24f`, with certain caveats about how the compiler handles floating point and . However, setting the rounding mode may be a time-consuming operation. If you bound the value, there may be similar “tricks” that take advantage of the natural rounding in floating point. – Eric Postpischil May 03 '13 at 16:26
  • http://codereview.stackexchange.com was designed for questions such as this. – Lightness Races in Orbit May 03 '13 at 16:26
  • What platform? Some platforms will have intrinsics that are designed to do this sort of thing fast (ARM). – Michael Dorgan May 03 '13 at 17:02
  • 3
    I have a question about your solution. When x_f is 4.0, x_even is 4, however, when x_f is 5.0, x_odd is 3. Is it really what you want? – Alper May 03 '13 at 17:22
  • @LightnessRacesinOrbit I think that the difference between a codereview question and a question about already working code on StackOverflow is that in a codereview question, the OP does not come with a specific aspect of the working code that s/he want to improve. Jav does (speed), and I like this question being here, for what it's worth. – Pascal Cuoq May 03 '13 at 19:25
  • @H2CO3: I don't have specification about negative numbers. But you are right, it they had been negative number, your assumption is correct. – Jav May 06 '13 at 08:08
  • @Alper: It is not exactly what I wanted, indeed :-/ Even if it's working like this for my usage. – Jav May 06 '13 at 08:09
  • @PascalCuoq: Code review is code review. – Lightness Races in Orbit May 06 '13 at 15:30

2 Answers2

0

Perhaps the ceil and floor function won't be necessary as transtypage from a double to an int is equivalent to the floor function for positive integer.

Try something like this for POSITIVE INTEGERs :

double k = 68.8 ; // Because we need something to seed with. 
int even  = ((int) k & ~1) ; // What you did 
int test = ((int) (k+1) & ~1) ; // Little trick
int odd = (test>k) ? odd+1 : odd - 1 ;  

I tested it on codepad, and it works well for on http://codepad.org/y3t0KgwW for C++, I think it will in C. If you test this solution, I'd be glad to know how fast it can be...

Notice that :

  • This is not a good answer as it shadows the existence of negative integers.
  • The range is limited to integers'.
  • I swapped odd and even numbers, I corrected it thank's to Chris' comment.
  • I'm just adding my humble stone :)
Pascail
  • 374
  • 2
  • 11
  • 2
    converting from double to int rounds towards 0, while floor rounds towards -infinity, so they're not the same for negative numbers. Also, you've swapped `odd` and `even` -- your code gives `odd==68` and `even==67` – Chris Dodd May 03 '13 at 23:21
  • Swapping is something I master. This is just because I'm French, and I always forget which one is Pair and which one is Impair. I guess I got it wrong this time. And by the way, this code obviously sink when given a negative integer. Thank you so much for pointing the swapping out. – Pascail May 04 '13 at 01:36
0

You don't explain what you mean by 'inferior', but assuming you mean 'greatest even/odd integer less than the given number', and assuming you have a 2s-complement machine, you want:

x_i = (int)floor(x_f);
x_even = x_i & ~1;
x_odd = x_i - (~x_i & 1);

If you want to avoid the implementation dependency of doing bitwise ops on possibly negative signed numbers, you could instead do it entirely in float:

x_even = 2.0 * floor(x_f * 0.5);
x_odd = x_even + 1.0 > x_f ? x_even - 1.0 : x_even + 1.0;

This also has the advantage of not overflowing for large numbers, though it does give you x_odd == x_even for large numbers (those too big for the floating point representation to represent an odd number).

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226