Easy way to keeping angles between -179 and 180 degrees

后端 未结 16 2651
长发绾君心
长发绾君心 2020-12-04 16:41

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:


/         


        
相关标签:
16条回答
  • 2020-12-04 17:07

    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]

    0 讨论(0)
  • 2020-12-04 17:09

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

    0 讨论(0)
  • 2020-12-04 17:09

    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))));
    }   
    
    0 讨论(0)
  • 2020-12-04 17:10

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

    0 讨论(0)
  • 2020-12-04 17:10
    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;
    
    0 讨论(0)
  • 2020-12-04 17:11

    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]
    }
    
    0 讨论(0)
提交回复
热议问题