35
votes

Is there a way to safety and simply deal with angle wrap with the minimum number of case statements.

Angle wrap occurs when using a particular representation for angle (either 0-360 deg or -180 - 180 deg (or equivalent in radians)) and you wrap over the angle. For example say you have an angle of -170, and you subtract 50 deg. You mathematically add up to -220 but should actually be +140 deg.

Obviously you can check for this using:

if (deg < -180) { 180 - abs(deg + 180); }

or similar. But firstly you need multitudes of checks and secondly it doesn't work if you wrap twice.

The second case where this is prevalent is in the interpolation between two angles.

For Example, say I have an angle of -170 deg and 160 deg and I want halfway in between them. A common way to do this is ang1 + 0.5(ang2-ang1) but in the example i have provided it will cause the angle to be -5 deg when it should be 175.

Is there a common way to handle angle wrap in these scenarios?

6
Are you after performance? Or just the shortest solution that works? - Mysticial
Not performance, more like simplicity and ease of reading. (Of course the complex number may not be the case but i would still like to have a look at that). - Fantastic Mr Fox
So you want to normalize an angle to [0, 360)? - Mysticial
Well to be honest i would prefer to deal with normalising to [-180, 180) - Fantastic Mr Fox
With respect to your edit: There's two ways to bisect an angle. And they differ by exactly 180 degrees. The algorithm you have gives one of them. Add/subtract 180 degrees and you get the other one. At this point you should wrap them to [-180,180). You now have two angles, you can pick the "better" of them by seeing which is "closest" to the initial two angles. - Mysticial

6 Answers

66
votes

For completeness I'll include both [0, 360) and [-180, 180) normalizations.

You will need #include <math.h>.


Normalize to [0,360):

double constrainAngle(double x){
    x = fmod(x,360);
    if (x < 0)
        x += 360;
    return x;
}

Normalize to [-180,180):

double constrainAngle(double x){
    x = fmod(x + 180,360);
    if (x < 0)
        x += 360;
    return x - 180;
}

The pattern should be easy enough to recognize to generalize to radians.


Angle Bisection:

double angleDiff(double a,double b){
    double dif = fmod(b - a + 180,360);
    if (dif < 0)
        dif += 360;
    return dif - 180;
}
double bisectAngle(double a,double b){
    return constrainAngle(a + angleDiff(a,b) * 0.5);
}

This should bisect an angle on the "smaller" side. (warning: not fully tested)

20
votes

I find using remainder() from the math library is convenient. Given an angle a, to constrain it to -180, 180 you can just do:

remainder(a, 360.0);

and change the 360.0 to 2.0 * M_PI for radians

7
votes

Normalise an angle to range [-180, 180)

deg -= 360. * std::floor((deg + 180.) * (1. / 360.));

Normalise an angle to range [0, 360)

deg -= 360. * std::floor(deg * (1. / 360.));

Examples:

deg = -90 -> [0, 360):

deg -= 360. * std::floor(-90 / 360.);
deg -= 360. * -1;
deg = 270

deg = 270 -> [-180, 180):

deg -= 360. * std::floor((deg + 180.) / 360.);
deg -= 360. * std::floor(480. / 360.);
deg -= 360. * 1.;
deg = -90;

See: http://en.cppreference.com/w/cpp/numeric/math/floor

5
votes

So if figured out a way to effectively do what i want using Mystical's approach to constraining the Angle. Here it is:

enter image description here

This seems to work with any example i can think of.

0
votes

I know this is an old thread, but try this on:

double angleRef(double thtIn, double thtRef){
  tht = fmod(thtIn + (180-thtRef),360) + (thtRef-180);
  return tht;
}

So as in your example, if A=-170 and B=160, then the angle halfway between them is A + 0.5*(angleRef(B,A) - A) = -185

or if you prefer A=160 and B=-170 A + 0.5*(angleRef(B,A) - A) = 175

Please forgive any c++ format errors, it is not my native language.

-1
votes

Map angle(+PI ~ -PI) to signed int value (or short value):

angle_signed_short = angle_float / PI * 0x7FFFF;

Then you can add or sub value as normal. Then map back:

angle_float = angle_signed_short * PI / 0x7FFFF;