与 Math.Round() 不一致

发布于 2024-12-02 17:58:43 字数 891 浏览 1 评论 0原文

我有两个函数旨在包含 (-180,180] 和 (-π,π] 之间的角度。其目的是给定从 -inf 到 +inf 的任何角度,它将在指定的间隔内保留等效角度。例如1550° 的角度是 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);
}

这会产生以下结果

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

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

,但这些结果都不是我想要的:

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

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

我尝试使用舍入方法,但仍然无法获得所需的结果,因为问题很明显。有时我处理的角度仅近似接近-π或π,并且我的结果出现不连续性,

关于如何最好地实现具有非包含下限和包含上限的角度环绕函数有什么建议吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

晒暮凉 2024-12-09 17:58:43

对于以度为单位的角度,如果 x 介于 -180 和 180 之间,则 180 - x 介于 0 到 360 之间。您想要的相当于要求 180 - x 介于 0(含)和 360(不含)之间。因此,一旦 180 - x 达到 360,我们就想将 360 添加到角度中。这给了我们:

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

对于以弧度表示的角度也是如此:

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

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);
走过海棠暮 2024-12-09 17:58:43

它没有解决舍入问题,但以下是我想要做的事情:

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;
}

注意:这种方式假设您希望“后面”为 180 度,而不是 -180 度。

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.

亽野灬性zι浪 2024-12-09 17:58:43

这不是模运算的情况吗?

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;
}

它通过了这个测试

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

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));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文