Products > Programming

Angle wrapping in code, "easy" problem, need to double check

(1/3) > >>

Infraviolet:
Is this always guaranteed to work for integers:

int16_t Clamp360(int16_t value){
value= value % 360;
if(value >= 0){
return value;
}else{
return 360 - (0 - value);
}
}

By extension is this:

int16_t Clamp180(int16_t value){
value= (value+180) % 360;
if(value >= 0){
return value -180;
}else{
return 180 - (0 - value);
}
}

In languages like C and C++ where % is modulo, not truly a remainder and therefore returns negative results when negativeNum % positiveNum is done.

I'd rather avoid resorting to sin, cos and atan2 because this needs to run fast on a microcontroller in the end, so I'm trying to avoid trig functions or proper dot and cross products. And I'd also like not to have a while loop which may go on for a very long time if it subtracts 360 each time.

I've tested for all integers -1080 to +1080 and it plots out the right graphs for input vs output, but I've had nasty experiences with angle wrapping code before. I want to make sure this is right before I continue with any further code built around it.

Is this a guaranteed solution for signed integer variables? Will it also be one if floating point variables are used instead?
Thanks

P.S. any tips on angle-wrap-respecting code to determine if a supplied angle is inbetween (on the short side) another two supplied angles? Imagine "spinning a bottle" and then checking if the resulting angle is within a certain sector, the sectors not all being equally sized or spaced, so it needs to be a genuine check of "is it between these sector limit angles". It becomes fairly simple with trig or dot/cross products, but I need to avoid those for speed/memory/code space reasons. Thanks

ejeffrey:

--- Quote from: Infraviolet on April 27, 2024, 09:14:35 pm ---In languages like C and C++ where % is modulo, not truly a remainder and therefore returns negative results when negativeNum % positiveNum is done.

--- End quote ---

The C C++ way is generally called remainder, the sensible way is called modulo.

Your code looks right, although (360 - (0 -x)) can just be "x + 360" and it will do the same thing.

ejeffrey:
For floating point the % operator isn't defined and you need to use fmodf and friends.  They broadly work the same way.  One trap is that negative zero is an allowed result which may need to be special cased if you want to ensure that the result is always non negative and strictly less than 360.

golden_labels:
To avoid branching with 16-bit integer inputs, you may shift the input so the lowest input value congruent to 0 is actually 0, and % that:

--- Code: ---int16_t truncate360(int_fast32_t value) {
constexpr int_fast32_t lowestZero = INT32_C(360) * 91;
return (value + lowestZero) % 360;
}
--- End code ---
If you want to cover the very end at the bottom (below 32760), just shift it by another 360 (that is: 92 steps, not 91).

The above isn’t going to work nicely with arbitrary floats, because it relies on calculations being done in a type with a range at least twice the input. And even if the value fits, it may lose a bit of precision.

As for the “true” value: in both C or C++ word “modulo” is never used in reference to the % operator. The behavior of / and % is defined by this single identity: (a/b)*b + a%b == a. So the language isn’t even making a claim of supporting such operation. But, if it did, “mathematical” modular arithmetic is not inherently positive either. Some languages simply choose to use positive remainders, but it’s an arbitrary choice, not truth. It may be useful to limit operations to integers ring modulo N, but it’s not truth.

ejeffrey:

--- Quote ---mathematical” modular arithmetic is not inherently positive either

--- End quote ---

Obviously it's a matter of terminology and the only 100% unambiguous thing is to do is show the formula, but most sources use the word "remainder" to mean the thing that C and C++ does (share the sign of the dividend) and "modulo" mean the version where the result takes the sign of the divisor.  This includes the C standard which does call the result of the % operator the "remainder."  If you do "integer arithmetic modulo N" there are only N distinct values.  in C and C++, x % N can take on 2*N-1 possible values, which means that most of the distinct values have two possible representations.  Yes, it's a choice, but pretty inconvenient if you are doing anything you call modular arithmetic.

--- Quote ---The behavior of / and % is defined by this single identity: (a/b)*b + a%b == a

--- End quote ---

That is rather missing the point.  Everyone agrees that that the above formula has to be true for any sensible definition of %, and was required even in C89.  What C since C99 specify is that integer division truncates to zero rather than floor towards -infinity.  Once you specify division that way, in order to satisfy this formula you need to have negative numbers produce a negative remainder.  The other possible convention (and the one I would argue is far more commonly what you want) is where integer division is floor division, and the % operator acts like modulo.