Cara menggunakan konstanta PI di C ++

476

Saya ingin menggunakan fungsi konstanta PI dan trigonometri dalam beberapa program C ++. Saya mendapatkan fungsi trigonometri dengan include <math.h>. Namun, sepertinya tidak ada definisi untuk PI dalam file header ini.

Bagaimana saya bisa mendapatkan PI tanpa mendefinisikannya secara manual?

Etan
sumber
3
@tiwo, apakah Anda bertanya apa bedanya 3.14, 3.141592dan atan(1) * 4?
Nikola Malešević
21
Sebagai catatan tambahan, cmath harus digunakan dalam C ++ alih-alih math.h, yang untuk C.
juzzlin
4
Terkait longgar: lihat cise.ufl.edu/~manuel/obfuscate/pi.c tentang cara menghitung nilai PI langsung dari definisi.
lorro
2
Telah tiba di C ++ 20! stackoverflow.com/a/57285400/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 事件 法轮功

Jawaban:

537

Pada beberapa platform (terutama yang lebih tua) (lihat komentar di bawah), Anda mungkin perlu melakukannya

#define _USE_MATH_DEFINES

dan kemudian sertakan file header yang diperlukan:

#include <math.h>

dan nilai pi dapat diakses melalui:

M_PI

Dalam math.h(2014) saya ini didefinisikan sebagai:

# define M_PI           3.14159265358979323846  /* pi */

tetapi periksa math.hlebih lanjut. Ekstrak dari "lama" math.h(tahun 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.
 */

Namun:

  1. pada platform yang lebih baru (setidaknya pada 64 bit Ubuntu 14.04 saya) saya tidak perlu mendefinisikan _USE_MATH_DEFINES

  2. Pada platform Linux (baru-baru ini) ada long doublenilai - nilai yang juga disediakan sebagai Ekstensi GNU:

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */
Ferenc Deak
sumber
51
#define _USE_MATH_DEFINESdiikuti oleh #include <math.h>mendefinisikan M_PIdalam visual c ++. Terima kasih.
Etan
3
Bekerja dengan header cygwin juga.
Rob
24
Anda selalu dapat memasukkan cmathbukan math.h.
Richard J. Ross III
10
Bahkan setelah mendefinisikan _USE_MATH_DEFINESapakah GCC mengeluh karena __STRICT_ANSI__itu didefinisikan (mungkin Anda lulus -pedanticatau -std=c++11) yang tidak M_PIdapat didefinisikan, maka tentukan dengan -D__STRICT_ANSI__. Saat mendefinisikannya sendiri, karena itu C ++, alih-alih makro, Anda seharusnya constexpr auto M_PI = 3.14159265358979323846;.
legends2k
1
Pada 2018, Jawabannya harus diperbarui secara pasti untuk menggunakan <cmath> alih-alih <math.h>
jaskmar
170

Pi dapat dihitung sebagai atan(1)*4. Anda bisa menghitung nilainya dengan cara ini dan menyimpannya.

Konamiman
sumber
78
Untuk pengguna c ++ 11:constexpr double pi() { return std::atan(1)*4; }
matiu
41
-1: Hanya berfungsi jika atan(1)*4 == 3.141592653589793238462643383279502884(secara kasar). Saya tidak akan bertaruh. Normal dan gunakan literal mentah untuk mendefinisikan konstanta. Mengapa kehilangan presisi padahal Anda tidak perlu melakukannya?
Thomas Eding
29
Satu dapat menghindari operasi perkalian dengan atan2(0, -1);.
legends2k
44
@matiu atantidak constexpr.
R. Martinho Fernandes
45
Coba acos(-1)saja, tidak perlu atan2.
user541686
113

Anda juga bisa menggunakan boost, yang mendefinisikan konstanta matematika penting dengan akurasi maksimum untuk tipe yang diminta (yaitu float vs double).

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

Lihat dokumentasi peningkatan untuk lebih banyak contoh.

BuschnicK
sumber
184
Boost: Meningkatkan kompleksitas C ++ yang sudah tidak perlu sejak 1999!
Dan Moulding
47
Menarik dan sebagian benar. Di sisi lain, dorongan bisa sangat berguna pada waktu-waktu tertentu ...
BuschnicK
59
@DanMoulding: Uhm. Apakah C satu-satunya bahasa lain yang Anda tahu? Karena semua bahasa lain yang saya tahu, kecuali C, memiliki pustaka standar yang besarnya lebih besar dari C ++ '(misalnya Python, Haskell, C #, PHP, Delphi, Erlang, Java, ......). Dari pengalaman pribadi, opini-elitis not gonna use libsitu hama dan mungkin alasan nomor satu untuk perangkat lunak buruk yang ditulis dalam bahasa C ++.
Sebastian Mach
11
@Gracchus: Yup. C ++ tanpa pustaka (atau tanpa pustaka C ++ 11 yang baru) adalah, sebanyak saya menyukai bahasa itu dan sebanyak saya ingin mengkodekan semuanya sendiri, tidak terlalu produktif.
Sebastian Mach
14
Saya percaya katanya kompleksitas bukan ukuran . Agaknya merujuk pada a) 3 ruang nama bersarang, dan b) mendefinisikan pi sebagai fungsi templated daripada hanya konstanta normal.
Timmmm
83

Alih-alih mendapatkannya dari unit FPU pada chip:

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

double PI = get_PI();
Henrik
sumber
40
:-) mungkin bukan platform yang independen, tetapi solusi eksotis tambahan yang bagus!
Etan
3
Saya suka bagaimana Anda keluar dari kotak di sini;)
VivienLeger
1
Saya suka jawaban ini. Hal ini sangat berguna ketika menargetkan platform x86 yang lebih tua yang merupakan tren kecil belakangan ini di mana kompiler pengoptimalisasi tidak terlalu terlibat seperti yang modern. Terima kasih untuk Henrik ini!
Matt
49

Saya akan merekomendasikan hanya mengetik pi ke presisi yang Anda butuhkan. Ini tidak akan menambah waktu kalkulasi untuk eksekusi Anda, dan ini akan portabel tanpa menggunakan header atau #define. Menghitung Acos atau Atan selalu lebih mahal daripada menggunakan nilai yang telah dihitung.

const double PI  =3.141592653589793238463;
const float  PI_F=3.14159265358979f;
Alex
sumber
28
Ini adalah contoh yang bagus mengapa kita tidak harus mengambil pendekatan ini, kita orang membuat kesalahan, pembulatan, salin & tempel, dll. Saya pikir menggunakan M_PI adalah pendekatan yang tepat.
nacho4d
10
Jika seseorang melakukan ini di C ++ 11, buat consta constexpr.
legends2k
3
@ nacho4d Saya juga lebih suka M_PI jika tersedia, tetapi tidak semua sistem kompatibel dengan POSIX. Saya pikir pendekatan ini lebih baik daripada metode 4 * atan (1) untuk kasus-kasus di mana M_PI tidak tersedia.
m24p
2
"Menghitung AC atau atan selalu lebih mahal" tidak benar. Kompilator pengoptimal modern mana pun tahu semua tentang fungsi matematika standar dan dapat terus-menerus memperbanyaknya. Lihat misalnya goo.gl/BvdJyr
Nemo
2
@Nemo, Contoh konter : godbolt.org/g/DsAern Seperti yang telah dikatakan di tempat lain, tampaknya hanya GCC yang melakukan ini saat ini dan itu kemungkinan karena telah mendeklarasikan fungsi matematika dasar sebagai constexpr.
Parker Coates
47

Daripada menulis

#define _USE_MATH_DEFINES

Saya akan merekomendasikan menggunakan -D_USE_MATH_DEFINESatau /D_USE_MATH_DEFINESbergantung pada kompiler Anda.

Dengan cara ini Anda yakin bahwa bahkan dalam hal seseorang termasuk tajuk sebelum Anda melakukannya (dan tanpa #define) Anda masih akan memiliki konstanta alih-alih kesalahan kompiler yang tidak jelas yang perlu waktu lama untuk dilacak.

Matthieu M.
sumber
Tip yang bagus. Jika "Anda" adalah unit kompilasi maka tentu saja Anda dapat memastikan makro didefinisikan sebelum apa pun dimasukkan. Tetapi jika "Anda" adalah file tajuk, itu di luar kendali Anda.
Steve Jessop
3
Bahkan bahkan jika "Anda" adalah unit kompilasi ... tergantung pada urutan header adalah jalan terpendek menuju pemeliharaan mimpi buruk ...
Matthieu M.
1
Anda tidak harus bergantung pada urutan header. Tidak masalah apakah header menyertakan satu sama lain, asalkan Anda melakukan #define sebelum Anda #mengikutsertakan semuanya (setidaknya, dengan anggapan bahwa tidak ada yang #mengalihkannya). Hal yang sama berlaku untuk NDEBUG.
Steve Jessop
1
Masalah yang sangat umum dalam sebuah proyek adalah bahwa jika Anda mengkompilasi dengan Visual Studio misalnya Anda tidak tahu dalam urutan mana kompiler akan pergi melalui file Anda jadi jika Anda menggunakan <cmath>di tempat yang berbeda itu menjadi masalah besar (terutama jika disertakan oleh perpustakaan lain Anda termasuk). Akan jauh lebih baik jika mereka menempatkan bagian itu di luar penjaga sundulan tetapi juga tidak bisa berbuat banyak tentang itu sekarang. Direktif kompiler bekerja dengan sangat baik.
meneldal
40

Karena pustaka standar resmi tidak mendefinisikan PI konstan, Anda harus mendefinisikannya sendiri. Jadi jawaban untuk pertanyaan Anda "Bagaimana saya bisa mendapatkan PI tanpa mendefinisikannya secara manual?" adalah "Anda tidak - atau Anda mengandalkan beberapa ekstensi khusus-kompiler.". Jika Anda tidak khawatir tentang portabilitas Anda dapat memeriksa manual kompiler Anda untuk ini.

C ++ memungkinkan Anda untuk menulis

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

tetapi inisialisasi konstanta ini tidak dijamin statis. Namun kompiler G ++ menangani fungsi matematika tersebut sebagai intrinsik dan mampu menghitung ekspresi konstan ini pada waktu kompilasi.

sellibitze
sumber
6
Saya biasanya menggunakan ACOS (-1), seperti yang Anda katakan, mereka kompilasi-waktu dievaluasi. Ketika saya menguji M_PI, acos (-1) dan atan (1) * 4, saya mendapat nilai yang sama.
Mikha
2
Cara tradisional adalah dengan menggunakan 4*atan(1.): atanmudah diimplementasikan dan dikalikan dengan 4 adalah operasi yang tepat. Tentu saja, kompiler modern melipat (bertujuan untuk melipat) semua konstanta dengan presisi yang diperlukan, dan sangat masuk akal untuk menggunakan acos(-1)atau bahkan std::abs(std::arg(std::complex<double>(-1.,0.)))yang merupakan kebalikan dari formula Euler dan dengan demikian lebih estetis daripada yang terlihat (saya menambahkan abskarena saya tidak t ingat bagaimana pesawat kompleks dipotong atau jika itu didefinisikan sama sekali).
tobi_s
Hanya saja, tidak ada yang secara tidak sengaja menganggap Anda serius (lagi -_- '). Ini solusi yang mengerikan. Implementasi atan tidak didefinisikan oleh standar yang berarti implementasinya dan mungkin juga tergantung. Ini berarti bahwa angka bisa mengerikan, artinya Anda mungkin lebih baik menggunakan 3.14 secara umum. Lebih lanjut itu sangat mungkin lambat, bahkan untuk kasus khusus.
midjji
32

Dari halaman manual Posix di math.h :

   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
Joakim
sumber
3
Jawaban bagus tapi tautannya mati. Saya menyarankan yang ini sebagai gantinya.
Abderrahim Kitouni
30

C ++ 20 std::numbers::pi

Akhirnya, sudah tiba: http://eel.is/c++draft/numbers

Saya berharap penggunaannya menjadi seperti:

#include <numbers>
#include <iostream>

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

Saya akan mencobanya ketika dukungan datang ke GCC, GCC 9.1.0 dengan g++-9 -std=c++2amasih tidak mendukungnya.

Proposal yang diterima menjelaskan:

5.0. "Header" [header] Di tabel [tab: cpp.library.headers], <math>header baru perlu ditambahkan.

[...]

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

Ada juga std::numbers::etentu saja :-) Bagaimana cara menghitung konstanta Euler atau bertenaga Euler di C ++?

Konstanta ini menggunakan fitur templat variabel C ++ 14 : C ++ 14 Templat Variabel: apa tujuannya? Adakah contoh penggunaan?

Dalam versi draft sebelumnya, konstanta berada di bawah std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
27

Standar C ++ tidak memiliki konstanta untuk PI.

Banyak C ++ compiler menentukan M_PIdi cmath(atau di math.hC) sebagai ekstensi non-standar. Anda mungkin harus #define _USE_MATH_DEFINESsebelum dapat melihatnya.

RichieHindle
sumber
18

Saya akan lakukan

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

atau

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

Saya tidak akan mengetikkan π dengan presisi yang Anda butuhkan . Apa artinya itu? The presisi yang Anda butuhkan adalah ketepatan T, tapi kita tahu apa-apa tentangT .

Anda mungkin berkata: Apa yang sedang Anda bicarakan? Takan float, doubleatau long double. Jadi, ketikkan saja ketepatannya long double, yaitu

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

Tapi apakah Anda benar-benar tahu bahwa tidak akan ada tipe floating point baru dalam standar di masa depan dengan presisi yang lebih tinggi daripada long double ? Kamu tidak.

Dan itulah mengapa solusi pertama itu indah. Anda dapat yakin bahwa standar akan membebani fungsi trigonometri untuk tipe baru.

Dan tolong, jangan katakan bahwa evaluasi fungsi trigonometri pada inisialisasi adalah penalti kinerja.

0xbadf00d
sumber
1
Perhatikan itu arg(log(x)) == πuntuk semua 0 < x < 1.
0xbadf00d
Ini ide yang buruk. menggunakan constexpr template kelebihan per jenis, dengan cara itu Anda mendapatkan kesalahan kompilasi untuk memaksa Anda untuk mendefinisikannya jika tipe baru muncul. Ini juga umumnya mengerikan karena tipe trigonometri tidak terbatas pada tipe floating point. Jadi nikmati kesalahan atan (1) ... Standar tidak menjamin bahwa fungsi trigonometri menghitung nilai trigonometrik aktualnya dengan keakuratan jenisnya. Mereka umumnya tidak, dan semakin buruk dengan misalnya fastmath dan selalu sangat buruk untuk nilai-nilai khusus.
midjji
10

Saya menggunakan berikut ini di salah satu header umum saya di proyek yang mencakup semua pangkalan:

#define _USE_MATH_DEFINES
#include <cmath>

#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

#ifndef M_PIl
#define M_PIl (3.14159265358979323846264338327950288)
#endif

Di samping catatan, semua kompiler di bawah ini mendefinisikan konstanta M_PI dan M_PIl jika Anda menyertakan <cmath>. Tidak perlu menambahkan `#define _USE_MATH_DEFINES yang hanya diperlukan untuk VC ++.

x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+
Shital Shah
sumber
Dapatkah orang yang downvoter mengomentari apa yang salah dengan jawaban ini. Ini diteliti dan diuji dengan baik dan sedang digunakan dalam sistem nyata. Saya pasti ingin memperbaikinya jika ada sesuatu yang salah.
Shital Shah
1
FYI, kompiler Borland C ++ juga mendefinisikan M_PItanpa perlu_USE_MATH_DEFINES
Remy Lebeau
8

Saya biasanya lebih suka mendefinisikan sendiri: const double PI = 2*acos(0.0); karena tidak semua implementasi menyediakannya untuk Anda.

Pertanyaan apakah fungsi ini dipanggil pada saat runtime atau statis pada saat kompilasi biasanya tidak menjadi masalah, karena itu hanya terjadi sekali saja.

Sumudu Fernando
sumber
8
acos (-1) juga pi.
Roderick Taylor
3
Instruksi CPU sering kurang dan / atau latensi kurang untuk memuat operan langsung daripada membaca operan dari lokasi memori. Juga, hanya ekspresi yang diketahui pada waktu kompilasi yang dapat dihitung sebelumnya (maksud saya double x = pi * 1.5;dan sejenisnya). Jika Anda pernah berniat untuk menggunakan PI dalam matematika renyah dalam loop ketat, Anda lebih baik memastikan nilainya diketahui oleh kompiler.
Eugene Ryabtsev
7

Saya baru saja menemukan artikel ini oleh Danny Kalev yang memiliki tip bagus untuk C ++ 14 ke atas.

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

Saya pikir ini cukup keren (walaupun saya akan menggunakan PI presisi tertinggi di sana saya bisa), terutama karena template dapat menggunakannya berdasarkan jenis.

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>
Beta Jester
sumber
4

Nilai-nilai seperti M_PI, M_PI_2, M_PI_4, dll bukan standar C ++ sehingga constexpr tampaknya merupakan solusi yang lebih baik. Ekspresi const yang berbeda dapat dirumuskan yang menghitung pi yang sama dan itu menyangkut saya apakah mereka (semua) memberikan saya akurasi penuh. Standar C ++ tidak secara eksplisit menyebutkan cara menghitung pi. Karena itu, saya cenderung kembali ke mendefinisikan pi secara manual. Saya ingin berbagi solusi di bawah ini yang mendukung semua jenis fraksi pi dalam akurasi penuh.

#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;
}
Jeroen Lammertink
sumber
2
Sangat bagus. Mungkin perlu memiliki "l" atau "L" di akhir angka itu. Saya mendapatkan peringatan penyempitan dari compiler gcc saya di linux.
Berikan Rostig
2

Pada windows (cygwin + g ++), saya merasa perlu menambahkan flag -D_XOPEN_SOURCE=500untuk preprocessor untuk memproses definisi M_PIin math.h.

Papa Smurf
sumber
2
Ini bukan jawaban, tetapi komentar untuk jawaban fritzone.
0xbadf00d
2
@ 0xbadf00d: Ini adalah jawaban yang sepenuhnya mandiri yang menyediakan langkah-langkah yang diperlukan untuk M_PIbekerja pada platform tertentu. Itu bukan komentar pada jawaban untuk beberapa platform lain lagi bahwa jawaban untuk beberapa platform lain adalah komentar untuk yang satu ini.
Ben Voigt
2

C ++ 14 memungkinkan Anda melakukannya static constexpr auto pi = acos(-1);

Willy Goat
sumber
9
std::acosbukan a constexpr. Jadi, kode Anda tidak akan dikompilasi.
0xbadf00d
@ 0xbadf00d Saya mengkompilasinya dengan g ++
Willy Goat
12
@WillyGoat: Maka g ++ salah, karena acostidak constexprdalam C ++ 14, dan tidak diusulkan untuk menjadiconstexpr bahkan di C ++ 17
Ben Voigt
@ BenVoigt apakah ada fungsi matematika constexpr? Rupanya tidak: stackoverflow.com/questions/17347935/constexpr-math-functions
wcochran
1
@wcochran: Ada banyak fungsi matematika BARU constexpr, lihat misalnya ( github.com/kthohr/gcem ). Tetapi mereka tidak kompatibel dengan fungsi C dari nama yang sama, sehingga mereka tidak dapat mengambil alih nama lama.
Ben Voigt
2

Beberapa solusi elegan. Saya ragu bahwa ketepatan fungsi trigonometri sama dengan ketepatan jenis. Bagi mereka yang lebih suka menulis nilai konstan, ini berfungsi untuk g ++: -

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

Keakuratan 256 desimal digit harus cukup untuk jenis ganda panjang di masa mendatang. Jika diperlukan, kunjungi https://www.piday.org/million/ .

Jon Guiton
sumber
2
#include <cmath>
const long double pi = acos(-1.L);
gjerich
sumber
1

Kamu bisa melakukan ini:

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

Jika M_PIsudah didefinisikan dalam cmath, ini tidak akan melakukan apa pun selain memasukkan cmath. Jika M_PItidak didefinisikan (yang merupakan kasus misalnya di Visual Studio), itu akan mendefinisikannya. Dalam kedua kasus, Anda dapat menggunakanM_PI untuk mendapatkan nilai pi.

Nilai pi ini berasal dari qmath.h Qt Creator.

Donald Bebek
sumber
1

Anda bisa menggunakannya:

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

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

Konstanta Matematika tidak didefinisikan dalam Standar C / C ++. Untuk menggunakannya, Anda harus terlebih dahulu mendefinisikan _USE_MATH_DEFINESdan kemudian memasukkan cmathatau math.h.

Fazlı KUZU
sumber