Как использовать константу PI в C++
Я хочу использовать константу PI и тригонометрические функции в некоторых программах на C++. Я получаю тригонометрические функции с include <math.h>
, Однако в этом заголовочном файле, похоже, нет определения для PI.
Как я могу получить PI, не определяя его вручную?
25 ответов
На некоторых (особенно старых) платформах (см. Комментарии ниже) вам может понадобиться
#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.
*/
Тем не мение:
на более новых платформах (по крайней мере, на моем 64-битном Ubuntu 14.04) мне не нужно определять
_USE_MATH_DEFINES
На (недавних) платформах Linux есть
long double
значения также предоставляются как расширение GNU:# define M_PIl 3.141592653589793238462643383279502884L /* pi */
Пи можно рассчитать как atan(1)*4
, Вы можете рассчитать значение таким образом и кэшировать его.
C++20 std::numbers::pi
At last, it has arrived: http://eel.is/c++draft/numbers
I expect the usage to be like:
#include <numbers>
#include <iostream>
int main() {
std::cout << std::numbers::pi << std::endl;
}
I'll give it a try when support arrives to GCC, GCC 9.1.0 with g++-9 -std=c++2a
still doesn't support it.
The accepted proposal describes:
5.0. “Headers” [headers] In the table [tab:cpp.library.headers], a new
<math>
header needs to be added.[...]
namespace std { namespace math { template<typename T > inline constexpr T pi_v = unspecified; inline constexpr double pi = pi_v<double>;
There is also a std::numbers::e
of course:-) How to calculate Euler constant or Euler powered in C++?
These constants use the C++14 variable template feature: C++14 Variable Templates: what is their purpose? Any usage example?
In earlier versions of the draft, the constant was under std::math::pi
: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf
Получите это от блока FPU на чипе вместо этого:
double get_PI()
{
double pi;
__asm
{
fldpi
fstp pi
}
return pi;
}
double PI = get_PI();
Вы также можете использовать boost, который определяет важные математические константы с максимальной точностью для запрошенного типа (т. Е. Float против double).
const double pi = boost::math::constants::pi<double>();
Проверьте дополнительную документацию для дополнительных примеров.
Я бы порекомендовал просто набирать пи с нужной вам точностью. Это не добавит времени на вычисление и будет переносимым без использования заголовков или #defines. Расчет acos или atan всегда обходится дороже, чем использование предварительно рассчитанного значения.
const double PI =3.141592653589793238463;
const float PI_F=3.14159265358979f;
Вместо того чтобы писать
#define _USE_MATH_DEFINES
Я бы порекомендовал использовать -D_USE_MATH_DEFINES
или же /D_USE_MATH_DEFINES
в зависимости от вашего компилятора.
Таким образом, вы уверены, что даже в случае, если кто-то включит заголовок перед вами (и без #define), у вас останутся константы, а не неясная ошибка компилятора, которую вы будете искать целыми годами.
Поскольку официальная стандартная библиотека не определяет константу PI, вам придется определить ее самостоятельно. Итак, ответ на ваш вопрос "Как я могу получить PI, не определяя его вручную?" это "Вы не - или вы полагаетесь на некоторые специфичные для компилятора расширения". Если вы не беспокоитесь о переносимости, вы можете проверить руководство вашего компилятора для этого.
C++ позволяет писать
const double PI = std::atan(1.0)*4;
но инициализация этой константы не гарантируется быть статической. Однако компилятор G++ обрабатывает эти математические функции как встроенные и способен вычислять это константное выражение во время компиляции.
Со страницы руководства 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
Стандарт C++ не имеет константы для PI.
Многие компиляторы C++ определяют M_PI
в cmath
(или в math.h
для в) как нестандартное расширение. Возможно, вам придется #define _USE_MATH_DEFINES
прежде чем вы сможете увидеть это.
Я бы сделал
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
? Вы не
И именно поэтому первое решение красиво. Вы можете быть совершенно уверены, что стандарт перегружает тригонометрические функции для нового типа.
И, пожалуйста, не говорите, что оценка тригонометрической функции при инициализации является ухудшением производительности.
Я использую следующее в одном из моих общих заголовков в проекте, который охватывает все основы:
#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+
В стандартной библиотеке C++ 20 π определяется как std::numbers::pi_v
для
float
,
double
а также
long double
, например
#include <numbers>
auto n = std::numbers::pi_v<float>;
и может быть специализирован для определяемых пользователем типов.
Я обычно предпочитаю определять свои собственные: const double PI = 2*acos(0.0);
потому что не все реализации предоставляют его вам.
Вопрос о том, вызывается ли эта функция во время выполнения или является статическим во время компиляции, обычно не является проблемой, потому что в любом случае это происходит только один раз.
Я только что натолкнулся на эту статью Дэнни Калева, в которой есть отличный совет для 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>
Несколько изящных решений. Я сомневаюсь, что точность тригонометрических функций равна точности типов. Для тех, кто предпочитает писать постоянное значение, это работает для g++:-
template<class T>
class X {
public:
static constexpr T PI = (T) 3.14159265358979323846264338327950288419\
71693993751058209749445923078164062862089986280348253421170679821480865132823066\
47093844609550582231725359408128481117450284102701938521105559644622948954930381\
964428810975665933446128475648233786783165271201909145648566923460;
...
}
Точности в 256 десятичных знаков должно хватить для любого будущего типа long long long long double. Если требуется больше, посетите https://www.piday.org/million/.
Такие значения, как 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;
}
C++14 позволяет вам делать static constexpr auto pi = acos(-1);
Я запомнил число Пи до 11 цифр со времен колледжа (может быть, в старшей школе), так что это всегда мой предпочтительный подход:
#ifndef PI
#define PI 3.14159265359
#endif
Мне не нравятся #define, поскольку они представляют собой простые текстовые замены с нулевой безопасностью типов. Они также могут вызвать проблемы с использованием выражений, если скобки опущены, например
#define T_PI 2*PI
действительно должно быть
#define T_PI (2*PI)
Моим текущим решением этой проблемы является использование жестко запрограммированных значений для констант, например, в my_constants.hxx.
namespace Constants {
constexpr double PI = 3.141... ;
}
Однако я не задаю значения жестко (поскольку мне тоже не нравится такой подход), вместо этого я использую отдельную программу на Фортране для записи этого файла. Я использую Fortran, потому что он полностью поддерживает четырехкратную точность (C++ в VisualStudio — нет), а триггерные функции эквивалентны constexpr в C++. Например
real(8), parameter :: pi = 4*atan(1.0d0)
Без сомнения, другие языки могут быть использованы для того же самого.
Вы можете использовать это:
#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
.
В Windows (Cygwin + G ++) я нашел, что необходимо добавить флаг -D_XOPEN_SOURCE=500
для препроцессора обработать определение M_PI
в math.h
,
Вы можете сделать это:
#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.
15 знаков после запятой доставили человека на лунную поверхность и обратно. Все, что выходит за эти пределы, имеет астрономические масштабы. Сможете ли вы измерить это на практике в меньшем масштабе? Другие потратили месяцы на расчеты до триллионов цифр. Это бесполезно, кроме как попасть в книгу рекордов.
Знайте, что вы можете вычислить пи до произвольной длины, но сохранить практично.