Лучший способ получить соз (а) от греха (а) - PullRequest
7 голосов
/ 23 ноября 2011

Имея s как синус некоторого (неизвестного) угла "a", какой самый быстрый способ получить "косинус"?

Я знаю два нелепых способа:

c = cos(asin(s));

и

c = sqrt(1 - s*s);

Но я не знаю, как реализация функций cos (), asin () и sqrt () сравнивается по скорости. Как быстрее один над другим? Есть ли существенная разница между их реализациями в современных процессорах, скажем, между x86-64 и ARM с VFP? В конце концов, какое решение лучше?

Редактировать: Поскольку уже есть 3 несвязанных ответа, позвольте мне уточнить: у меня изначально нет угла, все, что у меня есть, это синус. Так что нет необходимости говорить мне поворачивать угол на 90 градусов, чтобы у меня было то же значение, что и у другой функции ...

Ответы [ 5 ]

8 голосов
/ 24 ноября 2011

Вот один из способов:

грех (а) ^ 2 + соз (а) ^ 2 = 1 (пифагор)

cos (a) = sqrt (1 - sin (a) ^ 2))

Вам необходимо выяснить различные квадранты (т.е. знак cos () отдельно). Это невозможно, если все, что у вас есть, это значение sin () (разные углы могут иметь одинаковый sin (), но cos () отличается знаком).

Как отмечали другие люди, на практике таблица поиска может быть самой быстрой. Зависит от того, какая точность вам требуется. Это почти наверняка будет быстрее, чем ваша версия cos (asin ()), а квадратный корень также может быть оптимизирован на практике .

  • SSE и ARM-NEON имеют инструкцию квадратного корня, но не имеют функций триггера.
  • x87 имеет квадратный корень и sin / cos, но не имеет обратных функций sin / cos. Квадратный корень быстрее, чем грех / cos.

При использовании Visual Studio 2010 производительность этого метода примерно в 6 раз выше, чем у версии на основе триггеров (с опцией быстрой плавающей запятой) на моем ноутбуке Core i3 (около 20 нс на вызов). Давайте посмотрим на сгенерированный код:

Опция быстрой с плавающей запятой с использованием квадратного корня:

; 15   :     return sqrt(1.0 - s*s);

    movsd   xmm1, QWORD PTR __real@3ff0000000000000
    mulsd   xmm0, xmm0
    subsd   xmm1, xmm0
    sqrtsd  xmm0, xmm1

Использование функций триггера:

; 22   :     return cos(asin(s));
call    ___libm_sse2_asin
jmp ___libm_sse2_cos

При переключении в точный режим с плавающей запятой сгенерированный код триггера использует различные функции (предположительно оптимизированные для SSE версии жертвуют точностью):

fld QWORD PTR _angle_sin$[esp+esi+65600]
call    __CIasin
call    __CIcos
fstp    QWORD PTR _angle_cos$[esp+esi+65600]
3 голосов
/ 24 ноября 2011

Лучше всего использовать eps * sqrt(1 - s * s), где eps - плюс или минус один.Это лучший способ

  • С точки зрения точности, , поскольку 1 - s * s остается близким к единице, и поэтому ошибка в вычислении квадратного корня минимальна. Вычисление косинусаАрксинус может потерять вашу точность, если вы находитесь в «близком к одному» случае (упражнение: 1 / почему? подсказка: помните, что грех максимален при pi / 2 и его производная исчезает 2 / попробуйте.) РЕДАКТИРОВАТЬ: Я говорил слишком быстро, поскольку sqrt(1 - s * s) имеет те же проблемы, когда s близок к единице.
  • С точки зрения скорости: квадратный корень прост (несколько арифметических операций для некоторого метода, подобного ньютону)), но неясно, что делает asin (вероятно, довольно дорого), cos, вероятно, будет на один порядок медленнее, чем sqrt, и, таким образом, один квадратный корень, вероятно, будет быстрее, чем эти две трансцендентные функциизвонки.В случае сомнений, профиль (но я готов поспорить, что деньги).

Забудьте о поиске таблиц, пока не докажете, что sqrt(1 - s * s) недостаточно быстро для вас (и даже там, выможно найти способы обменивать некоторую sqrt точность на скорость).

0 голосов
/ 23 ноября 2011

Если ваши функции sin / cos принимают радианы:

cos(x) === sin(x+pi/2)

Если ваши функции sin / cos принимают градусы:

cos(x) === sin(x+90)
0 голосов
/ 23 ноября 2011

визуализировать единичный круг с полярными координатами. r = 1, тета = (угол). тогда любая точка на единичной окружности в координатах X, Y (декартовых) равна (cos (тета), sin (тета)).

0 голосов
/ 23 ноября 2011

Sin и Cos - это просто одна и та же кривая, смещенная на половину радиана (или 90 градусов), поэтому:

sin(a) = cos(a + pi/2)
cos(a) = sin(pi/2 - a)

пи = 3,14159 ...

...