Як використовувати константу 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 безпосередньо з визначення.
lorro

Відповіді:


537

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

#define _USE_MATH_DEFINES

а потім додайте необхідний файл заголовка:

#include <math.h>

і значення pi можна отримати через:

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у візуальному 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

Pi можна обчислити як atan(1)*4. Ви можете таким чином обчислити значення та зберегти його в кеші.


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.
користувач541686

113

Ви також можете використовувати boost, який визначає важливі математичні константи з максимальною точністю для запитуваного типу (тобто float vs double).

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

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


184
Підвищення: Підвищення і без того непотрібної складності C ++ з 1999 року!
Dan Molding

47
Ловкий і частково правдивий. З іншого боку, прискорення може бути часом
надзвичайно

59
@DanMoulding: Ум. Є єдиною іншою мовою, яку ви знаєте? Тому що всі інші мови, які я знаю, крім C, мають стандартну бібліотеку, яка на величину перевищує C ++ '(наприклад, Python, Haskell, C #, PHP, Delphi, Erlang, Java, ......). З особистого досвіду, цей елітарний not gonna use libsопініон - шкідник, і, мабуть, причина номер один для поганого програмного забезпечення, написаного на C ++.
Себастьян Мах

11
@Gracchus: Так. C ++ без бібліотек (або без нових бібліотек C ++ 11) - це настільки, наскільки мені подобається ця мова, і стільки, скільки я хотів би сама кодувати, не дуже продуктивно.
Себастьян Мах

14
Я вважаю, що він сказав, що складність не розмір . Імовірно, мається на увазі а) 3 вкладені простори імен та б) визначення pi як шаблонна функція, а не просто нормальна константа.
Timmmm

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;

28
Це прекрасний приклад, чому ми не повинні застосовувати такий підхід, ми, люди, робимо помилки, округлення, копіювання та вставлення тощо. Я думаю, що використання M_PI - це правильний підхід.
nacho4d

10
Якщо хтось робить це в C ++ 11, зробіть constа constexpr.
legends2k

3
@ nacho4d Я теж віддаю перевагу M_PI, якщо він доступний, але не всі системи сумісні з POSIX. Я думаю, що цей підхід кращий, ніж метод 4 * atan (1) для тих випадків, коли M_PI недоступний.
m24p

2
"Розрахувати аксос або атан завжди дорожче" - це неправда. Будь-який сучасний оптимізуючий компілятор знає все про стандартні математичні функції і може постійно поширюватись через них. Дивіться, наприклад, goo.gl/BvdJyr
Немо,

2
@Nemo, Приклад лічильника: godbolt.org/g/DsAern Як вже було сказано в іншому місці, в даний час, мабуть, тільки GCC робить це, і це, ймовірно, тому, що він оголосив основні математичні функції як constexpr.
Parker Coates

47

Замість того, щоб писати

#define _USE_MATH_DEFINES

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

Таким чином ви впевнені, що навіть у випадку, якщо хтось включить заголовок перед вами (і без #define), ви все одно матимете константи замість неясної помилки компілятора, яку ви потребуватимуть у віках, щоб відстежити.


Гарна порада. Якщо "ви" є одиницею компіляції, то, звичайно, ви можете переконатися, що макрос визначений до того, як щось буде включено. Але якщо "ви" - це файл заголовка, це не під вашим контролем.
Стів Джессоп

3
Насправді, навіть якщо ви "є" компіляційною одиницею ... залежно від впорядкованості заголовків - це найкоротший шлях до кошмару технічного обслуговування ...
Матьє М. М.

1
Не потрібно залежати від впорядкування заголовків. Не має значення, чи включають заголовки один до одного, за умови, що ви робите #define перед тим, як # включити щось взагалі (принаймні, якщо припустити, що нічого # не визначає). Те саме стосується і NDEBUG.
Стів Джессоп

1
Дуже поширеною проблемою в проекті є те, що якщо ви, наприклад, компілюєте Visual Studio, ви не знаєте, в якому порядку компілятор буде проходити через ваші файли, тому якщо ви використовуєте <cmath>в різних місцях, це стане великим болем (особливо якщо він включений до іншої бібліотеки, яку ви включаєте). Було б набагато краще, якби вони поставили цю частину поза захисними заголовками, але зараз це зробити не можна багато. Директива про компілятор справді працює досить добре.
meneldal

40

Оскільки офіційна стандартна бібліотека не визначає постійну ІП, вам доведеться її визначити самостійно. Тож відповідь на ваше запитання "Як я можу отримати ІП, не визначаючи його вручну?" - "Ви цього не робите - або Ви покладаєтесь на деякі розширення, що стосуються компілятора." Якщо вас не турбує портативність, ви можете перевірити посібник свого компілятора для цього.

C ++ дозволяє писати

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

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


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що я не ' t пам'ятайте, як розрізається складна площина або якщо це взагалі визначено).
tobi_s

Просто так ніхто випадково не думає, що ви серйозно (знову -_- '). Це жахливе рішення. Реалізація атана не визначається стандартом, що означає її реалізацію, і, можливо, залежить від hw. Це означає, що числові дані можуть бути жахливими, тобто загалом вам може бути краще використовувати 3.14. Далі його цілком можливо повільно, навіть для особливих випадків.
midjji

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. "Заголовки" [заголовки] У таблиці [вкладка: 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


27

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

Багато C ++ компілятори визначають M_PIв cmath(або math.hна C) в якості нестандартного розширення. Можливо, вам доведеться, #define _USE_MATH_DEFINESперш ніж побачити це.


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? Ви цього не робите.

І тому перше рішення прекрасне. Ви можете бути повністю впевнені, що стандарт перевантажить тригонометричні функції для нового типу.

І, будь ласка, не кажіть, що оцінка тригонометричної функції при ініціалізації - покарання продуктивності.


1
Зауважте, що arg(log(x)) == πдля всіх 0 < x < 1.
0xbadf00d

Це жахлива ідея. використовувати перевантажений шаблон constexpr для кожного типу, таким чином ви отримаєте помилку компіляції, щоб змусити вас визначити її, якщо з’явиться новий тип. Її також взагалі жахливо, оскільки типи тригів не обмежуються типами з плаваючою точкою Тож насолоджуйтесь помилкою atan (1) ... Стандарт не гарантує, що тригонометричні функції обчислюють свої фактичні тригонометричні значення до точності типу. Вони, як правило, цього не роблять, і це погіршується, наприклад, у швидкій матчі і завжди особливо погано для спеціальних значень.
midjji

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+

Чи може комерційний коментар прокоментувати те, що не так у цій відповіді. Це добре досліджено та перевірено та використовується у реальній системі. Я напевно хотів би покращити його, якщо щось не так.
Shital Shah

1
FYI, компілятори 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 ++, тому кращий висновок здається кращим рішенням. Можна сформулювати різні вирази const, які обчислюють однакові пі, і це стосується мене, чи забезпечують вони (усі) повну точність. Стандарт C ++ прямо не вказує, як обчислити pi. Тому я схильний повертатися до визначення пі вручну. Я хотів би поділитися рішенням нижче, яке підтримує всі види фракцій pi в повній точності.

#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_PIв math.h.


2
Це не відповідь, а коментар до відповіді fritzone.
0xbadf00d

2
@ 0xbadf00d: Це повністю окрема відповідь, яка забезпечує кроки, необхідні для M_PIроботи на певній платформі. Це вже не коментар до відповіді для якоїсь іншої платформи, а відповідь для іншої платформи - коментар до цієї.
Ben Voigt

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 точних цифр точності повинно вистачити для будь-якого майбутнього довгого довгого подвійного типу. Якщо потрібно більше, відвідайте https://www.piday.org/million/ .



1

Ви можете зробити це:

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

Якщо M_PIце вже визначено в cmath, це не буде робити нічого іншого, ніж включати cmath. Якщо M_PIце не визначено (що є, наприклад, у Visual Studio), він визначить це. В обох випадках ви можете використовуватиM_PI для отримання значення pi.

Це значення pi походить від qmath.h Qt Creator.


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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.