Easy way to keeping angles between -179 and 180 degrees

Viewed 60010

Is there an easy way to convert an angle (in degrees) to be between -179 and 180? I'm sure I could use mod (%) and some if statements, but it gets ugly:


//Make angle between 0 and 360
angle%=360;

//Make angle between -179 and 180
if (angle>180) angle-=360;

It just seems like there should be a simple math operation that will do both statements at the same time. I may just have to create a static method for the conversion for now.

16 Answers
// reduce the angle  
angle =  angle % 360; 

// force it to be the positive remainder, so that 0 <= angle < 360  
angle = (angle + 360) % 360;  

// force into the minimum absolute value residue class, so that -180 < angle <= 180  
if (angle > 180)  
    angle -= 360;  

Try this instead!

atan2(sin(angle), cos(angle))

atan2 has a range of [-π, π). This takes advantage of the fact that tan θ = sin θ / cos θ, and that atan2 is smart enough to know which quadrant θ is in.

Since you want degrees, you will want to convert your angle to and from radians:

atan2(sin(angle * PI/180.0), cos(angle * PI/180.0)) * 180.0/PI

Update My previous example was perfectly legitimate, but restricted the range to ±90°. atan2's range is the desired value of -179° to 180°. Preserved below.


Try this:

asin(sin(angle)))

The domain of sin is the real line, the range is [-1, 1]. The domain of asin is [-1, 1], and the range is [-PI/2, PI/2]. Since asin is the inverse of sin, your input isn't changed (much, there's some drift because you're using floating point numbers). So you get your input value back, and you get the desired range as a side effect of the restricted range of the arcsine.

Since you want degrees, you will want to convert your angle to and from radians:

asin(sin(angle * PI/180.0)) * 180.0/PI

(Caveat: Trig functions are bazillions of times slower than simple divide and subtract operations, even if they are done in an FPU!)

Not that smart, too, but no if.

angle = (angle + 179) % 360 - 179;

But I am not sure how Java handles modulo for negative numbers. This works only if -1 modulo 360 equals 359.

UPDATE

Just checked the docs and a % b yields a value between -(|b| - 1) and +(|b| - 1) hence the code is broken. To account for negative values returned by the modulo operator one has to use the following.

angle = ((angle + 179) % 360 + 360) % 360 - 179;

But ... no ... never ... Use something similar to your initial solution, but fixed for values smaller then -179.

I'm a little late to the party, I know, but...

Most of these answers are no good, because they try to be clever and concise and then don't take care of edge cases.

It's a little more verbose, but if you want to make it work, just put in the logic to make it work. Don't try to be clever.

int normalizeAngle(int angle)
{
    int newAngle = angle;
    while (newAngle <= -180) newAngle += 360;
    while (newAngle > 180) newAngle -= 360;
    return newAngle;
}

This works and is reasonably clean and simple, without trying to be fancy. Note that only zero or one of the while loops can ever be run.

Maybe not helpful, but I always liked using non-degree angles.

An angle range from 0 to 255 can be kept in bounds using bitwise operations, or for a single byte variable, simple allowed to overflow.

An angle range from -128 to 127 isn't quite so easy with bitwise ops, but again, for a single-byte variable, you can let it overflow.

I thought it was a great idea many years back for games, where you're probably using a lookup table for angles. These days, not so good - the angles are used differently, and are float anyway.

Still - maybe worth a mention.

A short way which handles negative numbers is

double mod = x - Math.floor((x + 179.0) / 360) * 360;

Cast to taste.

BTW: It appears that angles between (180.0, 181.0) are undefined. Shouldn't the range be (-180, 180] (exclusive, inclusive]

int angle = -394;

// shortest
angle %= 360;
angle = angle < -170 ? angle + 360 : (angle > 180 ? angle - 380 : angle);

// cleanest
angle %= 360;
if (angle < -179) angle += 360;
else if (angle > 180) angle -= 360;

How about

(angle % 360) - 179

This will actually return different results than the naive approach presented in the question, but it will keep the angle between the bounds specified. (I suppose that might make this the wrong answer, but I will leave it here in case it solves another persons' similar problem).

Here is my contribution. It seems to work for all angles with no edge issues. It is fast. It can do n180[360000359] = -1 almost instantaneously. Notice how the Sign function helps select the correct logic path and allows the same code to be used for different angles.

Ratch

n180[a_] := 
 If[Abs[Mod[a, If[Sign[a] == 0, 360, Sign[a] 360]]] <= 180, 
  Mod[a, If[Sign[a] == 0, 360, Sign[a] 360]], 
  Mod[a, If[Sign[a] == 0, 360, -Sign[a] 360]]]

I don't know much Java, but I came across the same problem in Python. Most of the answers here were either for integers so I figured I'd add one that allows for floats.

def half_angle(degree):
    return -((180 - degree) % 360) + 180

Based off the other answers I'm guessing the function would looks something like this in Java (feel free to correct me)

int halfAngle(int degree) {
    return -Math.floorMod(180 - degree, 360) + 180
}

double halfAngle(double degree) {
    // Java doesn't have a built-in modulus operator
    // And Math.floorMod only works on integers and longs
    // But we can use ((x%n) + n)%n to obtain the modulus for float
    return -(((180 - degree) % 360 + 360) % 360) + 180
}

Replace int with float to your liking.

This works for any degree, both positive and negative and floats and integers:

half_angle(180) == 180
half_angle(180.1) == -179.9  // actually -179.89999999999998
half_angle(-179.9) == -179.9
half_angle(-180) = 180
half_angle(1) = 1
half_angle(0) = 0
half_angle(-1) = -1

Math explanation:

Because the modulo operator is open at the upper end, the value x % 360 is in the range [0, 360), so by using the negative angle, the upper end becomes the lower end. So -(-x%360) is in (-360, 0], and -(-x%360)+360 is in (0, 360].

Shifting this by 180 gives us the answer.

Related