Как C вычисляет sin () и другие математические функции?

189

Я просматривал .NET-дизассембли и исходный код GCC, но, похоже, не нашел реальной реализации sin() и других математических функций... они всегда, похоже, ссылаются на что-то еще.

Может ли кто-нибудь помочь мне найти их? Я чувствую, что маловероятно, что ВСЕ аппаратное обеспечение, которое C будет работать, поддерживает функции триггера в аппаратном обеспечении, поэтому там должен быть программный алгоритм, верно?


Мне известно о нескольких способах вычисления функций и написании моих собственных подпрограмм для вычисления функций с использованием серии taylor для удовольствия. Мне интересно, как это делают реальные, производственные языки, поскольку все мои реализации всегда на несколько порядков медленнее, хотя я считаю, что мои алгоритмы довольно умны (очевидно, это не так).

  • 2
    Обратите внимание, что эта реализация зависит. Вы должны указать, какая реализация вас больше всего интересует.
  • 11
    оба тега C и .net?
Показать ещё 3 комментария
Теги:
math
sine

20 ответов

159
Лучший ответ

В GNU libm реализация sin зависит от системы. Поэтому вы можете найти реализацию для каждой платформы где-нибудь в соответствующем подкаталоге sysdeps.

Один каталог включает реализацию на C, предоставленную IBM. С октября 2011 года это код, который фактически выполняется, когда вы вызываете sin() в типичную Linux-систему x86-64. Это, по-видимому, быстрее, чем инструкция сборки fsin. Исходный код: sysdeps/ieee754/dbl-64/s_sin.c, найдите __sin (double x).

Этот код очень сложный. Ни один программный алгоритм не является настолько быстрым, насколько это возможно, а также точным во всем диапазоне значений x, поэтому библиотека реализует множество различных алгоритмов, и первая задача - посмотреть на x и решить, какой алгоритм использовать. В некоторых регионах он использует то, что выглядит как знакомая серия Тейлора. Некоторые из алгоритмов сначала вычисляют быстрый результат, а затем, если это недостаточно точное, отбросьте его и отбросьте на более медленный алгоритм.

Старые 32-разрядные версии GCC/glibc использовали инструкцию fsin, что на удивление неточно для некоторых входов. Там увлекательное сообщение в блоге, иллюстрирующее это всего двумя строками кода.

Реализация fdlibm sin в чистом C намного проще, чем glibc, и хорошо прокомментирован. Исходный код: fdlibm/s_sin.c и fdlibm/k_sin.c

  • 28
    Чтобы увидеть, что это действительно код, работающий на x86: скомпилируйте программу, которая вызывает sin() ; введите gdb a.out , затем break sin , затем run , затем disassemble .
  • 5
    @ Генри: не делайте ошибку, думая, что это хороший код, хотя. Это действительно ужасно , не учитесь так кодировать!
Показать ещё 10 комментариев
70

OK детишки, время для профессионалов.... Это одна из моих самых больших жалоб у неопытных инженеров-программистов. Они приходят в вычисление трансцендентных функций с нуля (с использованием серии Тейлора), как если бы никто никогда не делал эти вычисления раньше в своей жизни. Не правда. Это хорошо определенная проблема, и тысячи раз были подобраны очень умными инженерами по программному обеспечению и аппаратным средствам и имеют четко определенное решение. В основном, большинство трансцендентных функций используют Чебышевские многочлены для их вычисления. Что касается того, какие полиномы используются, зависит от обстоятельств. Во-первых, Библия по этому вопросу - книга под названием "Компьютерные аппроксимации" Харта и Чейни. В этой книге вы можете решить, есть ли у вас аппаратный сумматор, множитель, разделитель и т.д., И решить, какие операции наиболее быстрые. например Если бы у вас был очень быстрый делитель, самым быстрым способом вычисления синуса может быть P1 (x)/P2 (x), где P1, P2 - многочлены Чебышева. Без быстрого делителя это может быть просто P (x), где P имеет гораздо больше членов, чем P1 или P2.... так что это будет медленнее. Итак, первый шаг - определить ваше оборудование и что он может сделать. Затем вы выбираете подходящую комбинацию полиномов Чебышева (как правило, вида cos (ax) = aP (x) для косинуса, например, снова, где P - многочлен Чебышева). Затем вы определяете, какую десятичную точность вы хотите. например если вам нужна точность в 7 цифр, вы смотрите это в соответствующей таблице в упомянутой книге, и она даст вам (для точности = 7.33) число N = 4 и число полиномов 3502. N - это порядок многочлена (так что p4.x ^ 4 + p3.x ^ 3 + p2.x ^ 2 + p1.x + p0), так как N = 4. Затем вы просматриваете фактическое значение значений p4, p3, p2, p1, p0 в конце книги под 3502 (они будут в плавающей запятой). Затем вы реализуете свой алгоритм в программном обеспечении в форме: (((p4.x + p3).x + p2).x + p1).x + p0 .... и вот как вы вычислили косинус до 7 знаков после запятой на этом оборудовании.

Обратите внимание, что большинство аппаратных реализаций трансцендентных операций в FPU обычно связаны с некоторыми микрокодами и такими операциями (в зависимости от аппаратного обеспечения). Многочлены Чебышева используются для большинства трансцендентных, но не для всех. например Квадратный корень быстрее использует двойную итерацию метода Ньютона raphson, используя первую таблицу поиска. Опять же, эта книга "Компьютерные аппроксимации" расскажет вам об этом.

Если вы планируете внедрить эти функции, я рекомендую всем, чтобы они получили копию этой книги. Это действительно библия для этих алгоритмов. Обратите внимание, что существуют пучки альтернативных средств для расчета этих значений, таких как кордики и т.д., Но они, как правило, лучше всего подходят для конкретных алгоритмов, где вам нужна только низкая точность. Чтобы гарантировать точность каждый раз, многочлены чебышева - путь. Как я уже сказал, четко определенная проблема. Решено уже 50 лет..... и вот как это делается.

Теперь, имея в виду, существуют методы, при которых полиномы Чебышева могут быть использованы для получения одного результата точности с полиномом низкой степени (например, пример для косинуса выше). Затем существуют другие методы интерполирования между значениями для повышения точности без необходимости перехода к гораздо большему многочлену, например, "Метод точных таблиц Gal". Этот последний метод - это то, на что ссылается сообщение, относящееся к литературе ACM. Но, в конечном счете, полиномы Чебышева - это то, что используется для получения 90% пути.

Enjoy.

  • 6
    Я не мог согласиться больше с первыми несколькими предложениями. Также стоит напомнить, что вычисление специальных функций с гарантированной точностью является сложной проблемой . Умные люди, о которых вы говорите, проводят большую часть своей жизни, занимаясь этим. Кроме того, в более техническом примечании полиномы min-max являются искомыми граалями, а полиномы Чебышева являются для них более простыми прокси.
  • 125
    -1 за непрофессиональный и бессвязный (и слегка грубый) тон, а также за то, что фактическое не избыточное содержание этого ответа, лишенное бессвязных и снисходительных слов, в основном сводится к: «Они часто используют многочлены Чебышева; см. Эту книгу для более подробной информации, это действительно хорошо! Что, вы знаете, вполне может быть абсолютно правильным, но на самом деле это не тот самостоятельный ответ, который мы хотим здесь, на SO. Сжатая таким образом, она сделала бы достойный комментарий по этому вопросу.
Показать ещё 2 комментария
58

Функции, такие как синус и косинус, реализуются в микрокоде внутри микропроцессоров. У чипов Intel, например, есть инструкции по сборке для них. Компилятор C генерирует код, который вызывает эти инструкции по сборке. (Напротив, компилятор Java не будет. Java оценивает функции триггера в программном обеспечении, а не аппаратном обеспечении, и поэтому он работает намного медленнее.)

Чипы не используют серию Тейлора для вычисления триггерных функций, по крайней мере, не полностью. Прежде всего, они используют CORDIC, но они также могут использовать короткую серию Тейлора для обработки результата CORDIC или для особых случаев как вычислительный синус с высокой относительной точностью для очень малых углов. Подробнее см. В этом https://stackoverflow.com/questions/345085/how-do-trigonometric-functions-work.

  • 9
    трансцендентные математические функции, такие как синус и косинус, могут быть реализованы в микрокоде или в виде аппаратных инструкций в современных 32-битных процессорах для настольных компьютеров и серверов. Это не всегда имело место до тех пор, пока в i486 (DX) все вычисления с плавающей запятой не были выполнены программным способом («soft-float») для серии x86 без отдельного сопроцессора. Не все из которых (FPU) включали трансцендентные функции (например, Weitek 3167).
  • 1
    Можете быть более конкретными? Как «отшлифовать» приближение, используя ряд Тейлора?
Показать ещё 6 комментариев
11

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

Численные методы могут аппроксимировать функции с произвольной точностью, и поскольку количество точности, которое у вас есть в плавающем числе, конечно, они довольно хорошо подходят для этих задач.

  • 11
    Реальная реализация, вероятно, не будет использовать серию Тейлора, поскольку есть более эффективные способы. Вам нужно только правильно аппроксимировать в области [0 ... pi / 2], и есть функции, которые обеспечат хорошее приближение более эффективно, чем ряд Тейлора.
  • 2
    @ Давид: я согласен. Я был достаточно осторожен, чтобы упомянуть слово «как» в моем ответе. Но разложение Тейлора - простое объяснение идеи методов, аппроксимирующих функции. Тем не менее, я видел программные реализации (не уверен, что они были оптимизированы), которые использовали серии Тейлор.
Показать ещё 1 комментарий
9

Это сложный вопрос. Intel-подобный процессор семейства x86 имеет аппаратную реализацию функции sin(), но он является частью FPU x87 и больше не используется в 64-битном режиме (там используются регистры SSE2). В этом режиме используется программная реализация.

Существует несколько таких реализаций. Один находится в fdlibm и используется в Java. Насколько мне известно, реализация glibc содержит части fdlibm и другие части, предоставленные IBM.

Программные реализации трансцендентных функций, таких как sin(), обычно используют аппроксимации полиномами, часто получаемыми из серии Тейлора.

  • 3
    Регистры SSE2 не используются для вычисления sin () ни в режиме x86, ни в режиме x64, и, конечно, sin рассчитывается аппаратно независимо от режима. Эй, это 2010 год, в котором мы живем :)
  • 7
    @Igor: это зависит от того, на какую математическую библиотеку вы смотрите. Оказывается, что наиболее оптимизированные математические библиотеки на x86 используют программные реализации SSE для sin и cos , которые быстрее, чем аппаратные инструкции на FPU. Более простые, более наивные библиотеки, как правило, используют инструкции fsin и fcos .
Показать ещё 9 комментариев
9

используйте серия taylor и попытайтесь найти связь между терминами серии, чтобы вы не вычисляли вещи снова и снова

вот пример для cosinus:

double cosinus(double x,double prec)
{
    double t , s ;
    int p;
    p = 0;
    s = 1.0;
    t = 1.0;
    while(fabs(t/s) > prec)
    {
        p++;
        t = (-t * x * x) / ((2 * p - 1) * (2 * p));
        s += t;
    }
    return s;}

используя это, мы можем получить новый член суммы, используя уже использованную (мы избегаем факториала и x ^ 2p)

описание http://img514.imageshack.us/img514/1959/82098830.jpg

8

Для sin в частности, использование расширения Тейлора даст вам:

sin (x): = x - x ^ 3/3! + x ^ 5/5! - x ^ 7/7! +... (1)

вы продолжаете добавлять термины до тех пор, пока разница между ними не будет ниже принятого уровня допуска или просто за конечное количество шагов (быстрее, но менее точно). Примером может быть что-то вроде:

float sin(float x)
{
  float res=0, pow=x, fact=1;
  for(int i=0; i<5; ++i)
  {
    res+=pow/fact;
    pow*=-1*x*x;
    fact*=(2*(i+1))*(2*(i+1)+1);
  }

  return res;
}

Примечание: (1) работает из-за аппроксимации sin (x) = x для малых углов. Для больших углов вам нужно рассчитать все больше и больше условий, чтобы получить приемлемые результаты. Вы можете использовать аргумент while и продолжать с определенной точностью:

double sin (double x){
    int i = 1;
    double cur = x;
    double acc = 1;
    double fact= 1;
    double pow = x;
    while (fabs(acc) > .00000001 &&   i < 100){
        fact *= ((2*i)*(2*i+1));
        pow *= -1 * x*x; 
        acc =  pow / fact;
        cur += acc;
        i++;
    }
    return cur;

}
6

Многочлены Чебышева, как упоминалось в другом ответе, являются многочленами, где наибольшее различие между функцией и полиномом как можно меньше. Это отличный старт.

В некоторых случаях максимальная ошибка - это не то, что вас интересует, а максимальная относительная ошибка. Например, для синусоидальной функции ошибка вблизи x = 0 должна быть намного меньше, чем для больших значений; вам нужна небольшая относительная ошибка. Таким образом, вы вычисляете полином Чебышева для sin x/x и умножаем этот многочлен на x.

Затем вам нужно выяснить, как оценить многочлен. Вы хотите оценить его таким образом, чтобы промежуточные значения были небольшими, и поэтому ошибки округления небольшие. В противном случае ошибки округления могут стать намного большими, чем ошибки в полиноме. И с функциями, такими как функция синуса, если вы небрежны, возможно, результат, который вы вычисляете для sin x, больше, чем результат для sin y, даже когда x < у. Поэтому необходим тщательный выбор порядка расчета и вычисления верхних границ погрешности округления.

Например, sin x = x - x ^ 3/6 + x ^ 5/120 - x ^ 7/5040... Если вы наивно вычисляете sin x = x * (1 - x ^ 2/6 + x ^ 4/120 - x ^ 6/5040...), то эта функция в круглых скобках уменьшается, и произойдет, что если y - следующее большее число к x, то иногда sin y будет меньше, чем sin x. Вместо этого вычислите sin x = x - x ^ 3 * (1/6 - x ^ 2/120 + x ^ 4/5040...), где этого не может быть.

При расчете полиномов Чебышева, например, обычно нужно округлить коэффициенты до двойной точности. Но в то время как полином Чебышева оптимален, полином Чебышева с коэффициентами, округленными до двойной точности, не является оптимальным полиномом с коэффициентами двойной точности!

Например, для sin (x), где вам нужны коэффициенты для x, x ^ 3, x ^ 5, x ^ 7 и т.д., вы делаете следующее: Вычислите наилучшее приближение sin x с полиномом (ax + bx ^ 3 + cx ^ 5 + dx ^ 7) с более высокой, чем двойной точностью, затем округляя до двойной точности, давая A. Разница между a и A будет довольно большой. Теперь вычислим наилучшее приближение (sin x - Ax) с полиномом (b x ^ 3 + cx ^ 5 + dx ^ 7). Вы получаете разные коэффициенты, потому что они приспосабливаются к разности между a и A. Круглый b для двойной точности B. Тогда приближенно (sin x - Ax - Bx ^ 3) с полиномом cx ^ 5 + dx ^ 7 и т.д. Вы получите полином, который почти так же хорош, как и оригинальный полином Чебышева, но намного лучше, чем Чебышев, округленный до двойной точности.

Далее следует учитывать ошибки округления при выборе полинома. Вы нашли многочлен с минимальной ошибкой в ​​полиноме, игнорируя ошибку округления, но вы хотите оптимизировать ошибку полинома плюс округление. Когда у вас есть полином Чебышева, вы можете рассчитать границы ошибки округления. Скажем, что f (x) - ваша функция, P (x) - многочлен, а E (x) - ошибка округления. Вы не хотите оптимизировать | f (x) - P (x) |, вы хотите оптимизировать | f (x) - P (x) +/- E (x) |. Вы получите немного другой полином, который пытается сохранить ошибки полинома, где ошибка округления велика, и немного ослабляет ошибки полинома, где ошибка округления мала.

Все это позволит вам легко округлять ошибки не более чем в 0,55 раза по сравнению с последним битом, где +, -, *,/имеют ошибки округления не более 0,50 раз больше последнего бита.

  • 0
    Это хорошее объяснение того , как можно вычислить sin (х) эффективно, но это на самом деле не кажется , ответить на вопрос OP, который является конкретно о том , как общие библиотеки C / компиляторов вычислить.
5

Фактическая реализация библиотечных функций зависит от конкретного поставщика компилятора и/или библиотеки. Будет ли это сделано в аппаратном или программном обеспечении, будь то расширение Taylor или нет и т.д., Будет отличаться.

Я понимаю, что абсолютно никакой помощи.

  • 0
    +1 за фактически правильный ответ =)
4

В отношении тригонометрической функции, такой как sin(), cos(), tan(), после 5 лет не упоминалось о еще одном важном аспекте высококачественных триггерных функций: Уменьшение диапазона.

Ранним шагом в любой из этих функций является уменьшение угла в радианах до диапазона интервала 2 * π. Но π иррационально, поэтому простые сокращения, такие как x = remainder(x, 2*M_PI), вводят ошибку как M_PI, или машина pi, является аппроксимацией π. Итак, как сделать x = remainder(x, 2*π)?

Ранние библиотеки использовали расширенную точность или созданное программирование для получения качественных результатов, но все же в ограниченном диапазоне double. Когда было запрошено большое значение, например sin(pow(2,30)), результаты были бессмысленными или 0.0 и, возможно, с флагом ошибки , установленным на что-то вроде TLOSS полная потеря точности или PLOSS частичная потеря точности.

Хорошее сокращение диапазона больших значений до интервала, такого как -π to π, является сложной проблемой, которая соперничает с задачами самой основной функции триггера.

Хороший отчет Уменьшение аргумента для огромных аргументов: Хорошо до последнего бит (1992). Он хорошо освещает проблему: обсуждается необходимость и способы работы на разных платформах (SPARC, ПК, HP, 30+ других) и обеспечивает алгоритм решения, дает результаты качества для всех double от -DBL_MAX до DBL_MAX.

[изменить]

Если исходные аргументы находятся в градусах, но может быть большим значением, сначала используйте fmod() для повышения точности.

// sin(degrees2radians(x))
sin(degrees2radians(fmod(x,360)))

Различные триггерные идентификаторы и remquo() предлагают еще большее улучшение. как образец sind()

4

Как отмечали многие люди, это зависит от реализации. Но насколько я понимаю ваш вопрос, вас интересовало реальное программное обеспечение выполнение математических функций, но просто не удалось найти его. Если это так, то вы здесь:

  • Загрузите исходный код glibc из http://ftp.gnu.org/gnu/glibc/
  • Посмотрите файл dosincos.c, расположенный в распакованной папке glibc root\sysdeps\ieee754\dbl-64
  • Аналогичным образом вы можете найти реализации остальной математической библиотеки, просто найдите файл с соответствующим именем

Вы также можете посмотреть файлы с расширением .tbl, их содержимое представляет собой не что иное, как огромные таблицы предварительно вычисляемых значений различных функций в двоичной форме. Вот почему реализация так быстро: вместо вычисления всех коэффициентов любой серии, которую они используют, они просто выполняют быстрый поиск, что намного быстрее. Кстати, они используют серии Tailor для расчета синуса и косинуса.

Надеюсь, это поможет.

4

Если вы хотите реализовать в программном обеспечении, а не в аппаратном обеспечении, то найти окончательный ответ на этот вопрос - это глава 5 Численные рецепты. Моя копия находится в ящике, поэтому я не могу дать подробностей, но короткая версия (если я помню это право) заключается в том, что вы принимаете tan(theta/2) как свою примитивную операцию и вычисляете другие. Вычисление выполняется с помощью приближения рядов, но это то, что сходится гораздо быстрее, чем ряд Тейлора.

Извините, я не могу повторить больше, не взяв мою руку за книгу.

4

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

Обратите внимание, что эти программные подпрограммы не являются частью источников компилятора, но скорее будут найдены в библиотеке коррекционирования, такой как clib или glibc для компилятора GNU. См. http://www.gnu.org/software/libc/manual/html_mono/libc.html#Trig-Functions

Если вам нужен больший контроль, вы должны тщательно оценить, что вам нужно. Некоторые из типичных методов - это интерполяция поисковых таблиц, вызов сборки (который часто бывает медленным) или другие схемы приближения, такие как Newton-Raphson для квадратных корней.

4

Я попытаюсь ответить на случай sin() в программе на языке C, скомпилированной с компилятором GCC C на текущем процессоре x86 (скажем, Intel Core 2 Duo).

В языке C в стандартную библиотеку C входят общие математические функции, не включенные в сам язык (например, pow, sin и cos для питания, синуса и косинуса соответственно). Заголовки которых включены в math.h.

Теперь в системе GNU/Linux эти функции библиотеки предоставляются glibc (GNU libc или GNU C Library). Но компилятор GCC хочет, чтобы вы ссылались на математическую библиотеку (libm.so) с использованием флага компилятора -lm, чтобы включить использование этих математических функции. Я не уверен, почему он не является частью стандартной библиотеки C. Это будет программная версия функций с плавающей запятой или "soft-float".

Помимо этого: Причина того, что функции математики разделены, является исторической и предназначена только для уменьшения размера исполняемых программ в очень старых Unix-системах, возможно, до того, как были доступны общие библиотеки, насколько это возможно Я знаю.

Теперь компилятор может оптимизировать стандартную библиотечную функцию C sin() (предоставляемую libm.so), которая будет заменена вызовом собственной инструкции для встроенной функции sin() для CPU/FPU, которая существует как FPU (FSIN для x86/x87) на более новых процессорах, таких как Core 2 series (это довольно точно еще в i486DX). Это будет зависеть от флагов оптимизации, переданных компилятору gcc. Если компилятору было предложено написать код, который будет выполняться на любом i386 или более новом процессоре, он не будет делать такую ​​оптимизацию. Флаг -mcpu=486 должен сообщить компилятору, что безопасно сделать такую ​​оптимизацию.

Теперь, если программа выполнила версию программного обеспечения функции sin(), она будет делать это на основе CORDIC (компьютер с коррекцией по кругообороту) или алгоритм BKM, или, более вероятно, вычисление таблицы или мощности, которое обычно используется для расчета таких трансцендентных функций. [Src: http://en.wikipedia.org/wiki/Cordic#Application]

В любой недавней (начиная с версии 2.9x) версии gcc также имеется встроенная версия sin, __builtin_sin(), которая будет использоваться для замены стандартного вызова в библиотеке C-библиотеки в качестве оптимизации.

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

3

Всякий раз, когда такая функция оценивается, то на каком-то уровне есть, скорее всего, либо:

  • Таблица значений, которая интерполируется (для быстрых, неточных приложений - например, компьютерной графики).
  • Оценка серии, которая сходится к желаемому значению --- возможно, не серии Тейлора, скорее всего, что-то основано на фантастической квадратуре, такой как Кленшоу-Кертис.

Если нет поддержки аппаратного обеспечения, тогда компилятор, вероятно, использует последний метод, испускающий только код ассемблера (без символов отладки), вместо использования библиотеки ac, что затрудняет отслеживание фактического кода в вашем отладчик.

2

Нет ничего похожего на то, чтобы поразить источник и увидеть, как кто-то действительно сделал это в библиотеке общего пользования; рассмотрим, в частности, одну реализацию библиотеки C. Я выбрал uLibC.

Здесь функция sin:

http://git.uclibc.org/uClibc/tree/libm/s_sin.c

который выглядит так, как будто он обрабатывает несколько особых случаев, а затем выполняет некоторую редукцию аргумента, чтобы отобразить вход в диапазон [-pi/4, pi/4] (разделение аргумента на две части, большую часть и хвост) перед вызовом

http://git.uclibc.org/uClibc/tree/libm/k_sin.c

который затем работает на этих двух частях. Если хвост отсутствует, то приближенный ответ генерируется с использованием полинома степени 13. Если есть хвост, вы получаете небольшое корректирующее дополнение, основанное на принципе, что sin(x+y) = sin(x) + sin'(x')y

2

Если вы хотите посмотреть фактическую реализацию GNU этих функций на C, посмотрите последнюю строку glibc. См. Библиотека GNU C.

1

Вычисление синуса/косинуса/касательной на самом деле очень легко сделать с помощью кода с использованием серии Тейлора. Написание одного из них занимает 5 секунд.

Весь процесс можно суммировать с этим уравнением здесь: http://upload.wikimedia.org/math/5/4/6/546ecab719ce73dfb34a7496c942972b.png

Вот несколько подпрограмм, которые я написал для C:

double _pow(double a, double b) {
    double c = 1;
    for (int i=0; i<b; i++)
        c *= a;
    return c;
}

double _fact(double x) {
    double ret = 1;
    for (int i=1; i<=x; i++) 
        ret *= i;
    return ret;
}

double _sin(double x) {
    double y = x;
    double s = -1;
    for (int i=3; i<=100; i+=2) {
        y+=s*(_pow(x,i)/_fact(i));
        s *= -1;
    }  
    return y;
}
double _cos(double x) {
    double y = 1;
    double s = -1;
    for (int i=2; i<=100; i+=2) {
        y+=s*(_pow(x,i)/_fact(i));
        s *= -1;
    }  
    return y;
}
double _tan(double x) {
     return (_sin(x)/_cos(x));  
}
  • 3
    Это довольно плохая реализация, так как она не использует то, что последовательные члены ряда синуса и косинуса имеют очень простые отношения. Это означает, что можно уменьшить количество умножений и делений с O (n ^ 2) здесь до O (n). Дальнейшее сокращение достигается путем деления пополам и возведения в квадрат, как, например, это делается в математической библиотеке bc (POSIX multiprecision calculator).
  • 2
    Это также, кажется, не отвечает на вопрос как задано; OP спрашивает, как функции trig рассчитываются общими компиляторами / библиотеками C, а не для пользовательских переопределений.
Показать ещё 1 комментарий
0

Не используйте серии Тейлора. Многочлены Чебышева являются более быстрыми и точными, о чем указывает несколько человек выше. Вот реализация (первоначально из ROM ZX Spectrum): https://albertveli.wordpress.com/2015/01/10/zx-sine/

  • 2
    Кажется, это не отвечает на вопрос, который задают. OP спрашивает , как тригонометрические функции вычисляются общие компиляторы / библиотеки (и я уверен , что ZX Spectrum не отвечает), а не как они должны быть рассчитаны. Это могло бы быть полезным комментарием к некоторым из более ранних ответов, хотя.
  • 0
    Ах, ты прав. Это должен был быть комментарий, а не ответ. Я давно не пользовался SO и забыл, как работает система. В любом случае, я думаю, что реализация Spectrum актуальна, потому что у нее был очень медленный процессор, и скорость была существенной. Тогда лучший алгоритм, безусловно, все еще довольно хорош, поэтому для библиотек C было бы неплохо реализовать тригонометрические функции с использованием полиномов Чебышева.
-1

если вы хотите sin, тогда asm volatile ( "fsin": "= t" (vsin): "0" (xrads)); если вам нужен cos, тогда asm volatile ( "fcos": "= t" (vcos): "0" (xrads)); если вы хотите sqrt, тогда asm volatile ( "fsqrt": "= t" (vsqrt): "0" (значение)); поэтому зачем использовать неточный код, когда будут выполняться машинные инструкции.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню