Inconsistency with Math.Round()

我只是一个虾纸丫 提交于 2019-12-04 04:44:03

问题


I have two functions that are intended to contain angles between (-180,180] and (-π,π]. The intent is that given any angle from -inf to +inf it will retain the equivalent angle in the intervals specified. For example the angle for 1550° is 110°.

public double WrapBetween180(double angle)
{
    return angle - 360d * Math.Round(angle / 360d, MidpointRounding.AwayFromZero);
}
public double WrapBetweenPI(double angle)
{
    const double twopi = 2d * Math.PI;
    return angle - twopi * Math.Round(angle / twopi, MidpointRounding.AwayFromZero);
}

which yields the following results

WrapBetween180(-180) = -180
WrapBetween180( 180) =  180

WrapBetweenPI(-Math.PI) =  Math.PI
WrapBetweenPI( Math.PI) = -Math.PI

none of which is what I want. What I wanted is:

WrapBetween180(-180) =  180
WrapBetween180( 180) =  180

WrapBetweenPI(-Math.PI) =  Math.PI
WrapBetweenPI( Math.PI) =  Math.PI

I tryied playing around with the rounding methods, but still cannot get the desired results. The problem is pronounced because sometimes the angles I deal with are only approximately close to -π or π and I am getting discontinuities it my results.

Any suggestions on how to best implement angle wrapping functions with non-inclusive low limit and inclusive high limits?


回答1:


For the angle in degrees, if x is between -180 and 180, then 180 - x is between 0 and 360. What you want is equivalent to asking that 180 - x is between 0 (inclusive), and 360 (exclusive). So, as soon as 180 - x reaches 360, we want to add 360 to the angle. This gives us:

return angle + 360d * Math.Floor((180d - angle) / 360d);

Same thing for the angle in radians:

return angle + twopi * Math.Floor((Math.PI - angle) / twopi);



回答2:


It does not address the rounding issue, but here is how I would to what you want to do :

private static double ConvertAngle(double angle)
{
    bool isNegative = angle < 0;
    if (isNegative)
        angle *= -1;

    angle = angle % 360;
    if (isNegative)
        angle = -1 * angle + 360;

    if (angle > 180)
        angle = (angle - 360);

    return angle;
}

Note: This way supposes you want "behind" to be 180 degrees, not -180 degrees.




回答3:


Isn't this a case for a modulo operation?

private double Wrap180(double value)
{
    // exact rounding of corner values
    if (value == 180) return 180.0;
    if (value == -180) return 180.0;

    // "shift" by 180 and use module, then shift back.
    double wrapped = ((Math.Abs(value) + 180.0) % 360.0) - 180.0;

    // handle negative values correctly
    if (value < 0) return -wrapped;
    return wrapped;
}

It passes this tests

    Assert.AreEqual(170.0, wrap(-190.0));
    Assert.AreEqual(180.0, wrap(-180.0));
    Assert.AreEqual(-170.0, wrap(-170.0));
    Assert.AreEqual(0.0, wrap(0.0));
    Assert.AreEqual(10.0, wrap(10.0));
    Assert.AreEqual(170.0, wrap(170.0));
    Assert.AreEqual(180.0, wrap(180.0));
    Assert.AreEqual(-170.0, wrap(190.0));


来源:https://stackoverflow.com/questions/7271527/inconsistency-with-math-round

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!