45

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.

User1
  • 39,458
  • 69
  • 187
  • 265

16 Answers16

128
// 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;  
President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
  • 3
    That's the correct answer with the least calculations necessary. – tzot Aug 27 '10 at 07:50
  • Seems like the best solution here to me. – Steve Nov 02 '11 at 06:07
  • 6
    @PlatinumAzure: No, it is not redundant. Suppose the angle is -5000? – President James K. Polk Feb 16 '12 at 22:57
  • 8
    I'm rather shocked more people accepted the old double-while-loop approach over this... – Gurgadurgen Feb 14 '15 at 03:00
  • You could even remove the if statement by using `result -= 360 * ((result - 1) / 180);` Assuming this is all int math. – Gurgadurgen Feb 14 '15 at 03:37
  • what if my angle is a float rather than an integer? – Charbel Oct 04 '15 at 19:55
  • 9
    Note for anyone coming across this in the future (like me), in Java the `%` operator is remainder rather than modulus, so in languages like Python, the second line `angle = (angle + 360) % 360` isn't needed for it to work for negative numbers (although it doesn't do any harm). See: http://stackoverflow.com/questions/5385024/mod-in-java-produces-negative-numbers – Steve Jul 05 '16 at 15:09
  • This is a great approach that works with any "de-iterate" situation, not just angles. I'm wondering if there's a simple way to modify it, if you wanted to change the inclusive/exclusive boundaries. In other words `( ( value % maxValue ) + maxValue ) % maxValue` results in 0 <= n < maxValue, but say you wanted 0 <= n <= maxValue (in the case of angles, to include 360). Is there a better way than simply plopping `if( value == maxValue ){ return value; }` in front of the code? – Beejor Mar 11 '19 at 01:55
  • My hunch was that the first line (reduce) is redundant and is taken care of by the second line. Testing in another language (Rust) seems to indicate that that is true so I think you could shorten this further. – Peter Roehlen Feb 10 '20 at 05:08
  • @PeterRoehlen: possibly true in Rust, but not in java. The first reduce brings the angle it into a range where the addition can flip the sign. [Try it with -10000034](https://tio.run/##7VLLTsMwELznK/aClAjSpipCSG34g4pDj4iDEzthi2NH9iYSQvn2YOdVRKt@AXvxesbeWY/3xFoWn/hn39dNJjGHXDJr4cBQwXcALiwxcjgqgleDJSomQ79hqpQimg75GABI5@QOtk/JLljo9RoKbXIBSEAaMgH0IaDWFglbAUZUTpML8wBWO4oRJLBPp2J7X@xCKRyTe09GNwQV6UGsQoVVUwHLrJYNCWiZbLy0Re7W4eln@Xjz7DqYG0jBbZfaWMziLx6PFuLcXpyO/cyoEdQYNZIj2v3196A5Fij4dX//Dbxu4DS4k4@tRg7eifBIBlX59g7MlPa3kccvS6Ja6YZWtTtCUoXLZMebxMf2MYp2ty4sX3VxoQu6oO9/AA "Java (JDK) – Try It Online") – idbrii Dec 07 '21 at 20:45
  • It just works, not sure why this is pancaked under 7 inferior solutions. – Justas Nov 24 '22 at 15:21
26

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!)

Seth
  • 45,033
  • 10
  • 85
  • 120
  • 2
    +1 for taking advantage of limited-range functions for normalizing. A bit of overhead is involved, but this is by far the most concise answer that is actually correct. – Platinum Azure Feb 23 '10 at 19:43
  • Even better now, since the value is in the correct range. However, now you're doing three trig functions instead of two! – Seth Feb 23 '10 at 21:22
  • 1
    if you use http://commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/complex/Complex.html you can do Complex(0.0, angle).exp().log() which is arguably more concise. see http://stackoverflow.com/a/29237626/415404 for explanation. – Gus Sep 12 '15 at 22:24
12

I know that years have passed, but still.

This solution contains no loops, no subtracting, no modulo (allows to normalize to radians interval). Works for any input, including negative values, big values, edge cases.

double normalizedAngle = angle - (ceil((angle + M_PI)/(2*M_PI))-1)*2*M_PI;  // (-Pi;Pi]:
double normalizedAngle = angle - (ceil((angle + 180)/360)-1)*360;           // (-180;180]:

double normalizedAngle = angle - (floor((angle + M_PI)/(2*M_PI)))*2*M_PI;  // [-Pi;Pi):
double normalizedAngle = angle - (floor((angle + 180)/360))*360;           // [-180;180):
alexburtnik
  • 7,661
  • 4
  • 32
  • 70
11

This works with both negative and decimal numbers and doesn't require loops, nor trigonometric functions:

angle -= Math.floor(angle / 360 + 0.5) * 360

The result is in the [-180, 180) interval. For (-180, 180] interval, you can use this instead:

angle -= Math.ceil(angle / 360 - 0.5) * 360

Astronomino
  • 141
  • 1
  • 3
  • 2
    Just a note that `angle` must be a `float` for this or integer math will kick in. Use `angle -= Math.ceil((double)angle / 360 - 0.5) * 360` or something for best security. I love the answer, though. Should be #1 – Cory-G Jun 24 '14 at 21:26
  • I also like how this doesnt cause float-drift if the angle is already within the constraint. – Khlorghaal Dec 24 '18 at 10:56
  • This answer is the best combination of concise and mathematically simple. – Robert Penner Aug 09 '19 at 19:28
10

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.

ANeves
  • 6,219
  • 3
  • 39
  • 63
Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
8

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.

Platinum Azure
  • 45,269
  • 12
  • 110
  • 134
3

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.

3

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]

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
3

Here is an integer-only solution:

int normalize(int angle)
{
    angle %= 360;
    int fix = angle / 180; // Integer division!!
    return (fix) ? angle - (360 * (fix)) : angle;
}

Sometimes being clever is just more fun, Platinum Azure.

Cory-G
  • 1,025
  • 14
  • 26
3

I have made a formula for orientation of circular values

to keep angle between 0 and 359 is:

angle + Math.ceil( -angle / 360 ) * 360

but to keep between -179 to 180 formula can be:

angle + Math.ceil( (-angle-179) / 360 ) * 360

this will give its orientation shift about -179 keeping actual angle intact

generalized formula for shifting angle orientation can be:

angle + Math.ceil( (-angle+shift) / 360 ) * 360
Saad Ahmed
  • 1,077
  • 9
  • 9
2

Well, one more solution, this one with just one division and no loops.

static double normalizeAngle(double angle)
{
    angle %= 360.0; // [0..360) if angle is positive, (-360..0] if negative
    if (angle > 180.0) // was positive
        return angle - 360.0; // was (180..360) => returning (-180..0)
    if (angle <= -180.0) // was negative
        return angle + 360.0; // was (-360..180] => returning (0..180]
    return angle; // (-180..180]
}
Vlad
  • 35,022
  • 6
  • 77
  • 199
1

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).

pkaeding
  • 36,513
  • 30
  • 103
  • 141
1
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;
Matthew Whited
  • 22,160
  • 4
  • 52
  • 69
0

It is better to use library functions. They handle special cases like NaN and infinities.

public static double normalizeAngleDegrees(double angle) {
    return Math.toDegrees(Math.atan2(Math.sin(Math.toRadians(angle)), Math.cos(Math.toRadians(angle))));
}   
cohadar
  • 4,709
  • 2
  • 20
  • 21
0

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]]]
Ratch
  • 1
0

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.

Frank Vel
  • 1,202
  • 1
  • 13
  • 27