40

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?

Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175
  • Are you after performance? Or just the shortest solution that works? – Mysticial Jul 16 '12 at 04:40
  • 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 Jul 16 '12 at 04:41
  • So you want to normalize an angle to `[0, 360)`? – Mysticial Jul 16 '12 at 04:42
  • Well to be honest i would prefer to deal with normalising to [-180, 180) – Fantastic Mr Fox Jul 16 '12 at 04:44
  • 1
    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 Jul 16 '12 at 23:47
  • @Mystical i have added an answer using your answer, would you mind having a quick check to see if you think it will work? – Fantastic Mr Fox Jul 17 '12 at 00:15

7 Answers7

78

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)

Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • And if you want ease of reading, you could always just make it an inline function in a header. Something like `double constrainAngle(const double x);` – user1118321 Jul 16 '12 at 04:51
  • 1
    This does the wrong thing for negative numbers. (Or numbers less than -180 for the second case) - This is because `fmod(x,y)` has the same sign as x. – Michael Anderson Jul 16 '12 at 04:57
  • Hey Mystical, this is a great answer, unfortunately i haven't made my question very good and it does not really answer what i am trying to do. Please read the edit. – Fantastic Mr Fox Jul 16 '12 at 23:34
  • @Ben Answer updated. I think what you wanted was angle bisection. – Mysticial Jul 17 '12 at 00:13
  • Mystical, my apologies for not noting yours as correct, i didn't realise you had entered another answer. – Fantastic Mr Fox Jul 20 '12 at 03:05
  • In some cases the function constrainAngle from [0,360) will not produce the correct result. A small enough negative number will cause the floating point math to round and return an x value of 360. I tried this function with `double x = -0.0000001;` – user3220901 Jul 13 '18 at 17:45
  • You add 360 if it's less than zero. What if 'angle' is more than 360 negative? – Katastic Voyage May 17 '22 at 09:37
24

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

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
dmuir
  • 614
  • 4
  • 4
9

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

Charles Beattie
  • 5,739
  • 1
  • 29
  • 32
5

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.

Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175
  • Yeah, the equation is the same as mine - assuming the [180,180) version of `constrainAngle()`. So I think we're done. – Mysticial Jul 17 '12 at 00:18
0

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.

0

auto new_angle = atan2(sin(old_angle), cos(old_angle));

KosTTTT
  • 33
  • 4
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 04 '22 at 23:05
-1

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;
Steven
  • 2,437
  • 5
  • 32
  • 36
Jackal
  • 1