Каково определение гаммы (двойной x) и почему оно отличается в двух версиях gcc?

Из-за неудачных обстоятельств я обнаружил, что мои реализации стандартной библиотеки <math.h> и <cmath>(C++), по-видимому, содержат определение функции с прототипом, например:

double gamma(double x);

Хотя я не вижу его нигде в языковом стандарте (черновик, к которому у меня есть доступ).

Используя gcc v4.2.1 в Mac OS X, эта функция оценивается так же, как tgamma, что на самом деле является именем, данным ей в стандарте. (ссылка)

Но в gcc v4.6.3 в Ubuntu 12.04 эта функция имеет другое значение.

Я не понимаю, почему функция с именем gamma вообще компилируется, но почему результат у разных компиляторов разный?

Вот простая тестовая программа:

#include<iostream>
#include<sstream>
#include<math.h> // or <cmath>

int main(int argc, char **argv)
{
    std::stringstream conversionStream(argv[1]);
    double input;
    conversionStream >> input;
    std::cout << "gamma( " << input << " ) = " << gamma(input) << std::endl;
    return 0;
}

Скомпилируйте и запустите с 1 аргументом:

$ g++ -o gamma_test gamma_test.cpp 
$ ./gamma_test 1.0
gamma( 1 ) = 1

Но вывод на Ubuntu gcc v4.6.3 равен 0!


person NoahR    schedule 08.08.2013    source источник


Ответы (2)


Резюме: Историческая неразбериха изобилует; избегайте gamma() и используйте tgamma().

Эти функции реализует математическая библиотека, а не gcc (компилятор). Если вы видите различное поведение в MacOS и Ubuntu, возможно, это связано с тем, что Ubuntu использует glibc, а MacOS использует что-то другое.

В стандартной библиотеке ISO C нет функции с именем gamma.

Существуют стандартные функции, называемые lgamma и tgamma. Цитируя N1570 (последний проект стандарт ISO C 2011 г.), разделы 17.12.8.3 и 17.12.8.4:

#include <math.h>
double lgamma(double x);
float lgammaf(float x);
long double lgammal(long double x);

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

#include <math.h>
double tgamma(double x);
float tgammaf(float x);
long double tgammal(long double x);

Функции tgamma вычисляют гамма-функцию x. Ошибка домена или ошибка полюса может возникнуть, если x является отрицательным целым числом или нулем. Ошибка диапазона возникает, если величина x слишком велика, и может возникнуть, если величина x слишком мала.

Эти функции отсутствуют в стандарте ISO C 1990 года. Они были представлены C99.

Цитируя справочную страницу Linux для gamma:

Эти функции устарели: вместо них используйте функции tgamma(3) или lgamma(3), в зависимости от ситуации.

Определение гамма-функции см. в разделе tgamma(3).

  • #P11# <блочная цитата> #P12#
  • #P13# <блочная цитата> #P14#

и историческая справка:

В 4.2BSD была gamma(), которая вычисляла ln(|Gamma(|x|)|), оставляя знак Gamma(|x|) во внешнем целочисленном signgam. В 4.3BSD имя было изменено на lgamma(3), а справочная страница обещает

«Когда-нибудь в будущем название «гамма» будет реабилитировано и будет использоваться для функции «Гамма».

Это действительно произошло в 4.4BSD, где gamma() вычисляет функцию Gamma (без влияния на signgam). Однако это произошло слишком поздно, и теперь у нас есть tgamma(3), функция «истинной гаммы».

Поскольку gamma не является стандартной функцией C, компиляция с gcc -std=c99 -pedantic или gcc -std=c11 -pedantic должна выдавать как минимум предупреждение при любой попытке ее вызова.

Вероятно, вам следует использовать tgamma() (или lgamma(), если вам нужен натуральный логарифм) и избегать использования gamma().

Стандарт C, похоже, не говорит, что такое гамма-функция. справочная страница Linux tgamma() (но если вы пытаетесь использовать его, вы, вероятно, уже знаете, что это такое):

Гамма-функция определяется как

Gamma(x) = интеграл от 0 до бесконечности от t^(x-1) e^-t dt

Он определен для каждого действительного числа, кроме неположительных целых чисел.
Для неотрицательного интеграла m выполняется

Гамма(м+1) = м!

и, в более общем случае, для всех x:

Гамма(х+1) = х * Гамма(х)

Кроме того, следующее справедливо для всех значений x вне полюсов:

Гамма (x) * Гамма (1 - x) = PI / sin (PI * x)

person Keith Thompson    schedule 08.08.2013
comment
Спасибо за обстоятельный ответ! -std=c99 -pedandtic не генерирует никаких предупреждений для меня с gcc 4.2.1, так что эта часть бесполезна. Вставленные стандартные аннотации lgamma и tgamma на самом деле не добавляют ценности, потому что я думаю, что их легко найти, и они не являются проблемой. - person NoahR; 08.08.2013
comment
Хм. gcc -std=c99 -pedantic (не -pedandtic) предупреждает меня: неявное объявление функции 'gamma'. - person Keith Thompson; 08.08.2013
comment
ой. опечатка в моем комментарии, но не в моем использовании. Я получаю такое же предупреждение с gcc v4.6.3. - person NoahR; 08.08.2013

На некоторых платформах (linux и bsd до 4.2 или около того) gamma равно lgamma (журнал гамма-функции). На других платформах (osx и bsd после 4.4ish) это tgamma («настоящая» гамма-функция). Это исторический курьез.

На справочных страницах OS X есть примечание об этом:

gamma() и gamma_r() устарели и не должны использоваться. Вместо этого следует использовать функцию tgamma(). Обратите внимание, однако, что на некоторых платформах gamma() и gamma_r() исторически вычисляли журнал функции Gamma вместо самой функции Gamma. При переносе кода с таких платформ необходимо будет вместо этого использовать lgamma() или lgamma_r().

Короче говоря, просто не используйте gamma. Все-таки нестандартно. Вместо этого используйте стандартные функции tgamma и lgamma (или, возможно, расширение posix lgamma_r, которое является реентерабельным).

person Stephen Canon    schedule 08.08.2013