Избегание atan2 при расчете углов - точность atan2 - PullRequest
3 голосов
/ 07 января 2012

нам, разработчикам, очень часто нужно рассчитывать угол для выполнения поворота. Обычно мы можем использовать функцию atan2 (), но иногда нам нужна большая точность. Что ты тогда делаешь?

Я знаю, что теоретически atan2 является точным, но в моей системе (iOS) неточность около 0,05 радиана, так что это большая разница. Это не только моя проблема. Я видел похожие мнения.

Ответы [ 4 ]

3 голосов
/ 09 января 2012

atan2 используется для получения угла a от вектора (x,y). Если затем вы используете этот угол, чтобы применить вращение, вы будете использовать cos(a) и sin(a). Вы можете просто вычислить cos и sin, нормализуя (x, y), и оставить их вместо угла. Точность будет выше, и вы сэкономите много циклов, потерянных в тригонометрических функциях.

Edit. Если вам действительно нужен угол от (x, y), его можно вычислить с использованием вариантов CORDIC с необходимой вам точностью.

2 голосов
/ 11 февраля 2014

Использовать углы очень часто? Нет, ты не Из 10 раз, которые я видел, разработчик использовал углы, 7 раз он должен был использовать линейную алгебру и избегать тригонометрических функций.

Вращение лучше делать с матрицей, а не с углом. Смотрите также этот вопрос:

CGAffineTranformRotate atan2 неточность

2 голосов
/ 09 января 2012

В iOS я обнаружил, что стандартные операторы тригонометрии имеют точность примерно до 13 или 14 десятичных цифр, поэтому звучит очень странно, что вы видите ошибки порядка 0,05 радиана. Если вы можете создать код и конкретные значения, демонстрирующие это, пожалуйста, подайте отчет об ошибке о поведении (и опубликуйте код здесь, чтобы мы могли получить его запись).

Тем не менее, если вам действительно нужна высокая точность для ваших операторов тригонометрии, я изменил несколько подпрограмм, которые Дейв Делонг создал для своего DDMathParser кода. Эти процедуры используют NSDecimal для выполнения математических операций, давая вам до ~ 34 цифр десятичной точности, избегая при этом ваших стандартных проблем с плавающей запятой при представлении десятичных десятичных чисел. Вы можете скачать код для этих измененных подпрограмм с здесь .

NSDecimal версия atan() вычисляется с использованием следующего кода:

NSDecimal DDDecimalAtan(NSDecimal x) {
    // from: http://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Infinite_series

    // The normal infinite series diverges if x > 1
    NSDecimal one = DDDecimalOne();
    NSDecimal absX = DDDecimalAbsoluteValue(x);

    NSDecimal z = x;
    if (NSDecimalCompare(&one, &absX) == NSOrderedAscending) 
    {
        // y = x / (1 + sqrt(1+x^2))
        // Atan(x) = 2*Atan(y)
        // From: http://www.mathkb.com/Uwe/Forum.aspx/math/14680/faster-Taylor-s-series-of-Atan-x

        NSDecimal interiorOfRoot;
        NSDecimalMultiply(&interiorOfRoot, &x, &x, NSRoundBankers);
        NSDecimalAdd(&interiorOfRoot, &one, &interiorOfRoot, NSRoundBankers);
        NSDecimal denominator = DDDecimalSqrt(interiorOfRoot);
        NSDecimalAdd(&denominator, &one, &denominator, NSRoundBankers);
        NSDecimal y;
        NSDecimalDivide(&y, &x, &denominator, NSRoundBankers);

        NSDecimalMultiply(&interiorOfRoot, &y, &y, NSRoundBankers);
        NSDecimalAdd(&interiorOfRoot, &one, &interiorOfRoot, NSRoundBankers);
        denominator = DDDecimalSqrt(interiorOfRoot);
        NSDecimalAdd(&denominator, &one, &denominator, NSRoundBankers);
        NSDecimal y2;
        NSDecimalDivide(&y2, &y, &denominator, NSRoundBankers);

//        NSDecimal two = DDDecimalTwo();
        NSDecimal four = DDDecimalFromInteger(4);
        NSDecimal firstArctangent = DDDecimalAtan(y2);

        NSDecimalMultiply(&z, &four, &firstArctangent, NSRoundBankers);
    }
    else
    {
        BOOL shouldSubtract = YES;
        for (NSInteger n = 3; n < 150; n += 2) {
            NSDecimal numerator;
            if (NSDecimalPower(&numerator, &x, n, NSRoundBankers) == NSCalculationUnderflow)
            {
                numerator = DDDecimalZero();
                n = 150;
            }

            NSDecimal denominator = DDDecimalFromInteger(n);

            NSDecimal term;
            if (NSDecimalDivide(&term, &numerator, &denominator, NSRoundBankers) == NSCalculationUnderflow)
            {
                term = DDDecimalZero();
                n = 150;
            }

            if (shouldSubtract) {
                NSDecimalSubtract(&z, &z, &term, NSRoundBankers);
            } else {
                NSDecimalAdd(&z, &z, &term, NSRoundBankers);
            }

            shouldSubtract = !shouldSubtract;
        }
    }

    return z;
}

При этом используется приближение ряда Тейлора с некоторыми ярлыками для ускорения сходимости. Я считаю, что точность не может быть полных 34 цифр при результатах, очень близких к пи / 4 радианам, поэтому мне, возможно, все же придется это исправить.

Если вам нужна предельная точность, это вариант, но опять же то, о чем вы сообщаете, не должно происходить со значениями double, поэтому здесь есть что-то странное.

2 голосов
/ 07 января 2012

вы можете использовать atan2l, если long double имеет более высокую точность, чем double в вашей системе.

long double atan2l(long double y, long double x);
...