Несоответствие с Math.Round () - PullRequest
       3

Несоответствие с Math.Round ()

3 голосов
/ 01 сентября 2011

У меня есть две функции, которые должны содержать углы между (-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

Я попробовал поиграть с методами округления, но все еще не могу получить желаемые результаты. Проблема очевидна, потому что иногда углы, с которыми я имею дело, только приблизительно близки к -π или π, и я получаю разрывы, если мои результаты.

Какие-либо предложения о том, как наилучшим образом реализовать функции обтекания углов с неисключительным нижним пределом и включительно высокими ограничениями?

Ответы [ 3 ]

2 голосов
/ 01 сентября 2011

Для угла в градусах, если 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);
0 голосов
/ 02 сентября 2011

Разве это не случай для операции по модулю?

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));
0 голосов
/ 02 сентября 2011

Это не относится к проблеме округления, но вот как я хотел бы, чтобы вы хотели сделать:

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 градусов.

...