Как использовать константу PI в C ++

476

Я хочу использовать константу PI и тригонометрические функции в некоторых программах на C ++. Я получаю тригонометрические функции с include <math.h>. Однако в этом заголовочном файле, похоже, нет определения для PI.

Как я могу получить PI, не определяя его вручную?

Этан
источник
3
@tiwo, ты спрашиваешь, в чем разница 3.14, 3.141592и atan(1) * 4?
Никола Малешевич
21
В качестве дополнительного примечания, cmath следует использовать в C ++ вместо math.h, который предназначен для C.
juzzlin
4
Слабо связанные: см. Cise.ufl.edu/~manuel/obfuscate/pi.c о том, как рассчитать стоимость PI непосредственно из определения.
Лорро
3
Он прибыл в C ++ 20! stackoverflow.com/a/57285400/895245
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Ответы:

537

На некоторых (особенно старых) платформах (см. Комментарии ниже) вам может понадобиться

#define _USE_MATH_DEFINES

и затем включите необходимый заголовочный файл:

#include <math.h>

и значение пи можно получить через:

M_PI

По моему math.h(2014) это определяется как:

# define M_PI           3.14159265358979323846  /* pi */

но проверьте свой math.hна больше. Выписка из "старого" math.h(в 2009 году):

/* Define _USE_MATH_DEFINES before including math.h to expose these macro
 * definitions for common math constants.  These are placed under an #ifdef
 * since these commonly-defined names are not part of the C/C++ standards.
 */

Однако:

  1. на более новых платформах (по крайней мере, на моей 64-битной Ubuntu 14.04) мне не нужно определять _USE_MATH_DEFINES

  2. На (недавних) платформах Linux есть также long doubleзначения, предоставляемые как расширение GNU:

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */
Ференц Дик
источник
51
#define _USE_MATH_DEFINESс последующим #include <math.h>определением M_PIв Visual C ++. Спасибо.
Этан
3
Работает и с заголовками Cygwin.
Роб
24
Вы всегда можете включить cmathвместо math.h.
Ричард Дж. Росс III
10
Даже после определения, _USE_MATH_DEFINESесли GCC жалуется, это потому, что __STRICT_ANSI__он определен (возможно, вы прошли -pedanticили -std=c++11), который не M_PIможет быть определен, следовательно, не определите его -D__STRICT_ANSI__. Когда вы определяете это сами, так как это C ++, а не макрос, вы должны это сделать constexpr auto M_PI = 3.14159265358979323846;.
legends2k
1
Начиная с 2018 года, ответ должен быть определенно обновлен, чтобы использовать <cmath> вместо <math.h>
jaskmar
170

Пи можно рассчитать как atan(1)*4. Вы можете рассчитать значение таким образом и кэшировать его.

Konamiman
источник
78
Для пользователей c ++ 11:constexpr double pi() { return std::atan(1)*4; }
matiu
41
-1: работает только если atan(1)*4 == 3.141592653589793238462643383279502884(грубо говоря). Я бы не стал ставить на это. Будьте нормальными и используйте необработанный литерал для определения константы. Зачем терять точность, когда вам это не нужно?
Томас Эдинг
29
Можно избежать операции умножения с atan2(0, -1);.
legends2k
44
@matiu atanнет constexpr.
Р. Мартиньо Фернандес
45
Попробуйте acos(-1)вместо этого, не нужно atan2.
user541686
113

Вы также можете использовать boost, который определяет важные математические константы с максимальной точностью для запрошенного типа (т. Е. Float против double).

const double pi = boost::math::constants::pi<double>();

Проверьте дополнительную документацию для дополнительных примеров.

BuschnicK
источник
184
Повышение: повышение уже ненужной сложности C ++ с 1999 года!
Дэн Молдинг
47
Броский и отчасти правдивый. С другой стороны, повышение может быть феноменально полезным время от времени ...
BuschnicK
59
@DanMoulding: Хм. C - единственный другой язык, который вы знаете? Поскольку все другие языки, которые я знаю, кроме C, имеют стандартную библиотеку, которая на величины больше, чем C ++ '(например, Python, Haskell, C #, PHP, Delphi, Erlang, Java, ......). Исходя из личного опыта, это элитарное not gonna use libsмнение - вредитель и, вероятно, причина номер один для плохого программного обеспечения, написанного на C ++.
Себастьян Мах
11
@Gracchus: Да. C ++ без библиотек (или без новых библиотек C ++ 11), насколько мне нравится этот язык и сколько бы я ни хотел сам все кодировать, не очень продуктивен.
Себастьян Мах
14
Я считаю, что он сказал, что сложность не размер . Предположительно, ссылаясь на а) 3 вложенных пространства имен и б) определение pi как шаблонной функции, а не просто нормальной константы.
Тимммм
83

Получите это от блока FPU на чипе вместо этого:

double get_PI()
{
    double pi;
    __asm
    {
        fldpi
        fstp pi
    }
    return pi;
}

double PI = get_PI();
Хенрик
источник
40
:-), вероятно, не такая независимая от платформы, но приятное дополнительное экзотическое решение!
Этан
3
я люблю, как ты, хотя из коробки здесь;)
VivienLeger
1
Мне нравится этот ответ. Это особенно полезно при нацеливании на старые платформы x86, которые в последнее время не так популярны, когда оптимизирующие компиляторы задействованы не так страшно, как современные. Спасибо за это Хенрик!
Мэтт
49

Я бы порекомендовал просто набирать пи с нужной вам точностью. Это не добавит времени на вычисление и будет переносимым без использования заголовков или #defines. Расчет acos или atan всегда обходится дороже, чем использование предварительно рассчитанного значения.

const double PI  =3.141592653589793238463;
const float  PI_F=3.14159265358979f;
Alex
источник
28
Это отличный пример того, почему мы не должны использовать этот подход, мы, люди, совершаем ошибки, округляем, копируем и вставляем и т. Д. Я думаю, что использование M_PI - правильный подход.
nacho4d
10
Если вы делаете это в C ++ 11, сделайте consta constexpr.
legends2k
3
@ nacho4d Я тоже предпочитаю M_PI, если он доступен, но не все системы соответствуют POSIX. Я думаю, что этот подход лучше, чем метод 4 * atan (1) для случаев, когда M_PI недоступен.
m24p
2
«Расчет acos или atan всегда дороже» не соответствует действительности. Любой современный оптимизирующий компилятор знает все о стандартных математических функциях и может постоянно распространяться через них. См. Например, goo.gl/BvdJyr
Немо
2
@Nemo, пример Counter: godbolt.org/g/DsAern Как уже было сказано, кажется, что только GCC делает это в настоящее время, и это, вероятно, потому что он объявил основные математические функции как constexpr.
Паркер Коутс
47

Вместо того чтобы писать

#define _USE_MATH_DEFINES

Я бы порекомендовал использовать -D_USE_MATH_DEFINESили в /D_USE_MATH_DEFINESзависимости от вашего компилятора.

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

Матье М.
источник
Хороший совет. Если «вы» - единица компиляции, тогда, конечно, вы можете убедиться, что макрос определен до того, как что-либо будет включено. Но если «вы» - заголовочный файл, это вне вашего контроля.
Стив Джессоп
3
На самом деле, даже если «вы» - это единица компиляции ... в зависимости от порядка расположения заголовков это кратчайший путь к кошмару обслуживания ...
Матье М.
1
Вы не должны зависеть от порядка заголовков. Не имеет значения, включают ли заголовки друг друга, при условии, что вы #define делаете перед тем, как #include что-либо вообще (по крайней мере, предполагая, что ничего не #dedefs его). То же относится и к NDEBUG.
Стив Джессоп
1
Очень распространенная проблема в проекте заключается в том, что если вы, например, компилируете с помощью Visual Studio, вы не знаете, в каком порядке компилятор будет просматривать ваши файлы, поэтому, если вы используете его <cmath>в разных местах, это станет большой болью (особенно если он включен другой библиотекой, в которую вы входите). Было бы намного лучше, если бы они поместили эту часть за пределы охраны заголовка, но сейчас ничего не поделаешь. Директива компилятора работает довольно хорошо.
Менельдаль
40

Поскольку официальная стандартная библиотека не определяет константу PI, вам придется определить ее самостоятельно. Итак, ответ на ваш вопрос "Как я могу получить PI, не определяя его вручную?" это «Вы не - или вы полагаетесь на некоторые специфичные для компилятора расширения». Если вы не беспокоитесь о переносимости, вы можете проверить руководство вашего компилятора для этого.

C ++ позволяет писать

const double PI = std::atan(1.0)*4;

но инициализация этой константы не гарантируется быть статической. Однако компилятор G ++ обрабатывает эти математические функции как встроенные и способен вычислять это константное выражение во время компиляции.

sellibitze
источник
6
Я обычно использую acos (-1), как вы говорите, они оцениваются во время компиляции. Когда я тестировал M_PI, acos (-1) и atan (1) * 4, я получил одинаковые значения.
Михей
2
Традиционный способ заключается в использовании 4*atan(1.): atanего легко реализовать, а умножение на 4 - точная операция. Конечно, современные компиляторы складывают (стремятся сложить) все константы с требуемой точностью, и совершенно разумно использовать acos(-1)или даже то, std::abs(std::arg(std::complex<double>(-1.,0.)))что противоположно формуле Эйлера, и, таким образом, более эстетично, чем кажется (я добавил, absпотому что я не не помню, как сложная плоскость разрезается или определяется ли она вообще).
tobi_s
Просто так, что никто не думает, что ты серьезен (опять -_- '). Это ужасное решение. Реализация atan не определяется стандартом, означающим его реализацию, и, возможно, зависит от hw. Это означает, что цифры могут быть ужасными, а это значит, что вам лучше использовать 3.14 в целом. В дальнейшем это вполне возможно медленно, даже для особых случаев.
Миджи
32

Со страницы руководства Posix по математике :

   The  <math.h>  header  shall  provide for the following constants.  The
   values are of type double and are accurate within the precision of  the
   double type.

   M_PI   Value of pi

   M_PI_2 Value of pi/2

   M_PI_4 Value of pi/4

   M_1_PI Value of 1/pi

   M_2_PI Value of 2/pi

   M_2_SQRTPI
          Value of 2/ sqrt pi
Йоаким
источник
3
Хороший ответ, но ссылка мертва. Я предлагаю этот вместо этого.
Абдеррахим Китуни
30

C ++ 20 std::numbers::pi

Наконец-то пришло: http://eel.is/c++draft/numbers

Я ожидаю, что использование будет как:

#include <numbers>
#include <iostream>

int main() {
    std::cout << std::numbers::pi << std::endl;
}

Я попробую, когда поддержка придет в GCC, GCC 9.1.0 g++-9 -std=c++2aвсе еще не поддерживает его.

Принятое предложение описывает:

5.0. «Заголовки» [заголовки] В таблице [tab: cpp.library.headers] необходимо добавить новый <math>заголовок.

[...]

namespace std {
namespace math { 
  template<typename T > inline constexpr T pi_v = unspecified;
    inline constexpr double pi = pi_v<double>;

Существует также, std::numbers::eконечно :-) Как рассчитать постоянную Эйлера или питание Эйлера в C ++?

Эти константы используют функцию шаблонов переменных C ++ 14: Шаблоны переменных C ++ 14: какова их цель? Любой пример использования?

В более ранних версиях проекта константа находилась под std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
источник
27

Стандарт C ++ не имеет константы для PI.

Многие компиляторы C ++ определяют M_PIв cmath(или в math.hC) нестандартное расширение. Возможно, вам придется, #define _USE_MATH_DEFINESпрежде чем вы сможете увидеть это.

RichieHindle
источник
18

я бы сделал

template<typename T>
T const pi = std::acos(-T(1));

или

template<typename T>
T const pi = std::arg(-std::log(T(2)));

Я бы не стал вводить π с той точностью, которая вам нужна . Что это вообще должно означать? Точность вам нужно это точностьT , но мы ничего не знаем о T.

Вы можете сказать: о чем ты говоришь? Tбудет float, doubleили long double. Итак, просто введите точность long double, т.е.

template<typename T>
T const pi = static_cast<T>(/* long double precision π */);

Но знаете ли вы, что в будущем в стандарте не будет нового типа с плавающей запятой с еще большей точностью, чем long double ? Вы не

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

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

0xbadf00d
источник
1
Обратите внимание, что arg(log(x)) == πдля всех 0 < x < 1.
0xbadf00d
Это ужасная идея. используйте перегруженный шаблон для каждого типа constexpr, таким образом вы получите ошибку компиляции, чтобы заставить вас определить его, если появится новый тип. Это также вообще ужасно, потому что типы триггеров не ограничиваются типами с плавающей запятой. Так что наслаждайтесь ошибкой atan (1) ... Стандарт не гарантирует, что тригонометрические функции вычисляют свои действительные тригонометрические значения с точностью до типа. Они обычно не делают, и это ухудшается, например, с fastmath и всегда особенно плохо для специальных значений.
миджи
10

Я использую следующее в одном из моих общих заголовков в проекте, который охватывает все основы:

#define _USE_MATH_DEFINES
#include <cmath>

#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

#ifndef M_PIl
#define M_PIl (3.14159265358979323846264338327950288)
#endif

Напомним, что все приведенные ниже компиляторы определяют константы M_PI и M_PIl, если вы включаете их <cmath>. Нет необходимости добавлять `#define _USE_MATH_DEFINES, что требуется только для VC ++.

x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+
Шиталь шах
источник
Может ли downvoter прокомментировать, что не так с этим ответом. Это хорошо исследовано и проверено и используется в реальной системе. Я определенно хотел улучшить его, если что-то не так.
Шиталь Шах,
1
К сведению, компиляторы Borland C ++ также определяют M_PIбез необходимости_USE_MATH_DEFINES
Реми Лебо
8

Я обычно предпочитаю определять свое собственное: const double PI = 2*acos(0.0);потому что не все реализации предоставляют его вам.

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

Сумуду Фернандо
источник
8
acos (-1) также является пи.
Родерик Тейлор
3
Часто для загрузки немедленного операнда требуется меньше ресурсов процессора и / или меньше задержки, чем для чтения операнда из области памяти. Кроме того, только выражения, которые известны во время компиляции, могут быть предварительно вычислены (я имею в виду double x = pi * 1.5;и тому подобное). Если вы когда-нибудь намереваетесь использовать PI в хрустящей математике в замкнутых циклах, вам лучше убедиться, что это значение известно компилятору.
Евгений Рябцев
7

Я просто наткнулся на эту статью по Danny Kalev , который имеет большой наконечник для C ++ 14 и выше.

template<typename T>
constexpr T pi = T(3.1415926535897932385);

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

template<typename T>
T circular_area(T r) {
  return pi<T> * r * r;
}
double darea= circular_area(5.5);//uses pi<double>
float farea= circular_area(5.5f);//uses pi<float>
Бета шут
источник
4

Такие значения, как M_PI, M_PI_2, M_PI_4 и т. Д. Не являются стандартными C ++, поэтому constexpr кажется лучшим решением. Могут быть сформулированы различные выражения const, которые вычисляют один и тот же пи, и меня интересует, обеспечивают ли они (все) полную точность. Стандарт C ++ явно не упоминает, как рассчитать число пи. Поэтому я склоняюсь к определению числа «пи» вручную. Я хотел бы поделиться решением ниже, которое поддерживает все виды дробей с полной точностью.

#include <ratio>
#include <iostream>

template<typename RATIO>
constexpr double dpipart()
{
    long double const pi = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899863;
    return static_cast<double>(pi * RATIO::num / RATIO::den);
}

int main()
{
    std::cout << dpipart<std::ratio<-1, 6>>() << std::endl;
}
Йерун Ламмертинк
источник
2
Очень хорошо. Может быть необходимо иметь «l» или «L» в конце этого числа. Я получаю предупреждение от моего компилятора gcc на linux.
Грант Ростиг
2

В windows (cygwin + g ++) я счел необходимым добавить флаг -D_XOPEN_SOURCE=500для препроцессора для обработки определения M_PIin math.h.

Папа смурф
источник
2
Это не ответ, а комментарий к ответу fritzone.
0xbadf00d
2
@ 0xbadf00d: Это полностью автономный ответ, содержащий шаги, необходимые для M_PIработы на конкретной платформе. Это уже не комментарий к ответу для какой-либо другой платформы, а ответ для другой платформы - это комментарий к этой.
Бен Фойгт
2

C ++ 14 позволяет вам static constexpr auto pi = acos(-1);

Вилли Козел
источник
9
std::acosне constexpr. Итак, ваш код не будет компилироваться.
0xbadf00d
@ 0xbadf00d Я скомпилировал его с помощью g ++
Вилли Козел
12
@WillyGoat: Тогда g ++ неправильный, потому что acosне constexprв C ++ 14 и не предлагается constexprдаже в C ++ 17
Бен Фойгт
@BenVoigt, есть ли математические функции constexpr? Очевидно, нет: stackoverflow.com/questions/17347935/constexpr-math-functions
wcochran
1
@wcochran: есть много НОВЫХ математических функций, которые можно constexprпосмотреть, например, ( github.com/kthohr/gcem ). Но они не имеют обратной совместимости с одноименными функциями C, поэтому они не могут перенять старые имена.
Бен Фойгт
2

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

template<class T>
class X {
public:
            static constexpr T PI = (T) 3.14159265358979323846264338327950288419\
71693993751058209749445923078164062862089986280348253421170679821480865132823066\
47093844609550582231725359408128481117450284102701938521105559644622948954930381\
964428810975665933446128475648233786783165271201909145648566923460;
...
}

Точность 256 десятичных цифр должна быть достаточной для любого будущего типа long long long double. Если требуется больше, посетите https://www.piday.org/million/ .

Джон Гитон
источник
2
#include <cmath>
const long double pi = acos(-1.L);
gjerich
источник
1

Ты можешь сделать это:

#include <cmath>
#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

Если M_PIуже определено в cmath, это не будет делать ничего, кроме включения cmath. Если M_PIон не определен (как, например, в Visual Studio), он определит его. В обоих случаях вы можете использоватьM_PI чтобы получить значение числа пи.

Это значение pi взято из qmath.h Создателя Qt.

Дональд Дак
источник
1

Вы можете использовать это:

#define _USE_MATH_DEFINES // for C++
#include <cmath>

#define _USE_MATH_DEFINES // for C
#include <math.h>

Математические константы не определены в стандарте C / C ++. Чтобы использовать их, вы должны сначала определить, _USE_MATH_DEFINESа затем включить cmathили math.h.

Фазлы Кузу
источник