Определения sqrt, sin, cos, pow и т. Д. В cmath

Есть ли определения функций, таких как sqrt(), sin(), cos(), tan(), log(), exp() (это из math.h/cmath) доступно?

Я просто хотел знать, как они работают.

7 ответов

Решение

Это интересный вопрос, но чтение исходных текстов эффективных библиотек не продвинет вас слишком далеко, если вы не узнаете используемый метод.

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

  • Часто используются таблицы поиска
  • Тригонометрические функции часто реализуются с помощью алгоритма CORDIC (либо на процессоре, либо с помощью библиотеки). Обратите внимание, что обычно синус и косинус вычисляются вместе, я всегда удивлялся, почему стандартная библиотека С не обеспечивает sincos функция.
  • Квадратные корни используют метод Ньютона с некоторыми хитрыми уловками реализации: где-то в Интернете вы можете найти выдержку из исходного кода Quake с невероятной реализацией 1 / sqrt(x).
  • Экспоненциальный и логарифмический выражения используют exp(2^n x) = exp(x)^(2^n) и log2(2^n x) = n + log2(x), чтобы аргумент был близок к нулю (один для логарифма) и использовать приближение рациональной функции (обычно аппроксимации Паде). Обратите внимание, что этот же трюк может дать вам матричные экспоненты и логарифмы. Согласно @Stephen Canon, современные реализации предпочитают расширение Тейлора, а не приближение рациональных функций, где деление намного медленнее, чем умножение.
  • Другие функции могут быть выведены из этих. Реализации могут предоставлять специализированные процедуры.
  • pow (x, y) = exp (y * log (x)), поэтому pow не должен использоваться, когда y является целым числом
  • hypot(x, y) = abs(x) sqrt(1 + (y/x)^2), если x> y (hypot (y, x) в противном случае), чтобы избежать переполнения. atan2 вычисляется с вызовом sincos и немного логики. Эти функции являются строительными блоками для сложной арифметики.
  • Что касается других трансцендентных функций (гамма, эрф, бессел,...), пожалуйста, обратитесь к превосходной книге " Численные рецепты", 3-е издание, чтобы узнать некоторые идеи. Хорошие старые Abramowitz & Stegun также полезны. Новая версия на http://dlmf.nist.gov/.
  • Такие методы, как приближение Чебышева, непрерывное расширение дроби (на самом деле относящиеся к аппроксимациям Паде) или экономия степенных рядов, используются в более сложных функциях (например, если вы читаете исходный код для erf, bessel или gamma). Я сомневаюсь, что они имеют реальное применение в простых математических функциях, но кто знает. Консультируйтесь с Числовыми Рецептами для обзора.

Каждая реализация может отличаться, но вы можете проверить одну реализацию из исходного кода glibc (библиотеки GNU C).

изменить: поиск кода Google был переведен в автономный режим, поэтому старая ссылка, которую я имел, никуда не ведет.

Источники математической библиотеки glibc находятся здесь:

http://sourceware.org/git/?p=glibc.git;a=tree;f=math;h=3d5233a292f12cd9e9b9c67c3a114c64564d72ab;hb=HEAD

Посмотрите, как glibc реализует различные математические функции, полные магии, аппроксимации и сборки.

Обязательно посмотрите источники fdlibm. Они хороши тем, что библиотека fdlibm является самодостаточной, каждая функция хорошо документирована с подробными объяснениями используемой математики, и код очень легко читается.

Посмотрев много на математический код, я бы посоветовал не смотреть на glibc - за кодом часто довольно трудно следовать, и он во многом зависит от магии glibc. Математическая библиотека во FreeBSD намного легче читать, хотя иногда и медленнее (но не намного).

Для сложных функций основная трудность заключается в граничных случаях - правильная обработка nan/inf/0 уже сложна для реальных функций, но это сложный кошмар для сложных функций. Стандарт C99 определяет множество угловых корпусов, некоторые функции имеют 10-20 угловых корпусов. Вы можете посмотреть в приложении G обновленного стандартного документа C99, чтобы получить представление. Также есть проблема с long double, потому что его формат не стандартизирован - по моему опыту, вы должны ожидать довольно много ошибок с long double. Надеемся, что предстоящая пересмотренная версия IEEE754 с расширенной точностью улучшит ситуацию.

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

Использование: корень (число, корень, глубина)

Пример: root (16,2) == sqrt(16) == 4
Пример: root (16,2,2) == sqrt (sqrt (16)) == 2
Пример: root(64,3) == 4

Реализация на C#:

double root(double number, double root, double depth = 1f)
{
    return Math.Pow(number, Math.Pow(root, -depth));
}

Использование: Sqrt(число, глубина)

Пример: Sqrt(16) == 4
Пример: Sqrt(8,2) == sqrt(sqrt(8))

double Sqrt(double number, double depth = 1) return root(number,2,depth);

Автор: Imk0tter

Они почти всегда реализуются как системные вызовы. Если вы хотите посмотреть на источники, вам нужен доступ к источникам ОС, а это значит, что вам нужно взглянуть на ОС с открытым исходным кодом, такую ​​как Linux или BSD.

Другие вопросы по тегам