if statements V.S. % for wrapping values

Started by
9 comments, last by alvaro 13 years, 7 months ago
Hi Guys,

I was just wondering is there any efficiency / other reasons to use one or the other in the following two examples:

Example A:

angle= (angle+ diff) % 360;angle= angle< 0 ? angle= 360+angle: angle;


Example B:

float angleTemp = angle+diff;if(angleTemp  >= 360){ angleTemp -= 360;}if(angleTemp < 0) {  angleTemp += 360; }angle = angleTemp;


The modulator approach only just occurred to me and I was wondering whether it would be more or less efficient. Especially as some people would consider Example A less readable. My initial thought is that the difference would be more or less negligible but I thought i'd pose the question and find out.

-Thanks Chris
Advertisement
How about
angle= (angle+ diff+360) % 360;


Avoiding conditional statements could be faster, but never try to estimate the behavour of a CPU compiler, they are really good nowadways :-)
If you're after efficiency the best option is to use a new unit of angular measure, where a full circle is a power of two. For example 512 units in a circle. You can then simply do this:

angle = (angle + diff) & 511;

Edit: You can simplify even further if you use 2^32 as a full circle (assuming you store angles in 32-bit unsigned integers). If you do that the code becomes:

angle += diff;
If this is not only for efficiency, how about

while (angle < 0) angle += 360;while (angle >= 360) angle -= 360;


which also works in case some angular speed exceeds 360 by some factor.
I don't see how example A is any less readable, since it's usually pretty clear what you're trying to do with a modulus operator (especially in the context of angles and '360'). Plus it doesn't matter how large angle/diff are, you only need a single conditional to normalize the result. Using loops would work for example B (instead of 'if' statements which don't properly normalize all angle values), but then you linearize the amount of time it takes to normalize the angle and in the worst case you're counting down from INT_MAX/FLT_MAX/etc. in 360 increments.
An if that may or may not execute a simple statement is often converted into a cmov instruction, which doesn't involve a branch. That's probably the fastest thing to do. But, as always, only worry about this after a profiler tells you it's a bottleneck. Then check your compiler's output (in assembly code) and test what is faster in your particular usage.

Also, since you seem to be using floating-point numbers, % won't work: You should use fmod instead. Unfortunately the same brain-dead logic that lead to defining integer division as rounding towards zero made its way to the behavior of fmod, so you may have to do something about the negatives. Or you could roll your own:
double sane_fmod(double x, double y) {  return x - y * std::floor(x / y);}//...  angle = sane_fmod(angle, 2*Pi); // You should be using radians anyway
Thanks guys, thats really interesting!

Im not 100% sure how Adam_42s solution works, cause bit manipulation isnt a strong point. but the concept makes sense! so thanks for that I will have to play with that!

I also really liked Ashaman73 solution, its definitely an improvement on example A!

And phresnel, that is a very good point and a good improvement on example b I will definitely keep that in mind! As well as Zipsters comment on the potential problems which is another good point! I think Ashaman73s example is my favourite and I think I might stick to that for all future wrapping!

Thanks for all your comments its always good to see different ways of doing things and why! I appreciate all your input! Thanks, Chris
Oh, one more thing: You can almost always avoid using angles, if you use unit vectors instead. Your code is usually faster, shorter and easier to understand if you ditch angles altogether.

Oops.

angle = angle >= 0        ? angle % 360        : 360 - (-angle % 360);


Constant time for negative and constant time for positive numbers, and works for arbitrary large angles in the integer case.
IF statements are faster than modulo/division by at least an order of magnitude on most modern machines. Of course it won't make any difference unless the code is a hot spot, but modulo/division is slower than virtually any other operation.

This topic is closed to new replies.

Advertisement