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

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

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

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


person John Alexiou    schedule 01.09.2011    source источник
comment
Каждый дубль, который должен быть π, лишь приблизительно близок к π :-)   -  person Stefan Steinegger    schedule 01.09.2011
comment
Что должны делать эти методы? Я не вижу его цели ...   -  person Stefan Steinegger    schedule 01.09.2011
comment
Вы хотите сказать, что действительно хотите вернуть Math.Abs ​​(value1); где значение1 - результат ваших текущих функций?   -  person Mark Schultheiss    schedule 01.09.2011
comment
Связанная ссылка: stackoverflow.com/questions/846911/net-round- bug / 846917 # 846917.   -  person rajah9    schedule 01.09.2011
comment
@ Стефан. Я отредактировал вопрос с примером того, как эти функции должны работать. Все нормально, кроме тех случаев, когда близки к пределам.   -  person John Alexiou    schedule 01.09.2011
comment
@Mark - Несоответствие знаков возникает только для одного конкретного x.   -  person John Alexiou    schedule 01.09.2011


Ответы (3)


Для угла в градусах, если 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);
person Jeffrey Sax    schedule 01.09.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 градусов.

person Tipx    schedule 01.09.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));
person Stefan Steinegger    schedule 02.09.2011
comment
Мне нравится подход к использованию по модулю, но мне интересно, как он работает по сравнению с функцией Math.Floor() для больших чисел. Честно говоря, я не знаю, какой подход более надежный. - person John Alexiou; 15.09.2011