Apakah C ++ 11, 14, 17 atau 20 memperkenalkan konstanta standar untuk pi?

170

Ada masalah yang agak konyol dengan angka pi dalam C dan C ++. Sejauh yang saya tahu M_PIdidefinisikan dalam math.htidak diperlukan oleh standar apa pun.

Standar C ++ baru memperkenalkan banyak matematika yang rumit di perpustakaan standar - fungsi hiperbolik, std::hermitedan std::cyl_bessel_i, generator bilangan acak yang berbeda dan seterusnya dan seterusnya.

Apakah ada standar 'baru' yang memberikan konstanta untuk pi? Jika tidak - mengapa? Bagaimana semua matematika rumit ini bekerja tanpanya?

Saya menyadari pertanyaan serupa tentang pi dalam C ++ (mereka beberapa tahun dan standar lama); Saya ingin mengetahui kondisi masalah saat ini.

Saya juga sangat tertarik pada mengapa oh mengapa C ++ masih tidak memiliki pi konstan tetapi memiliki banyak matematika yang lebih rumit.

UPD: Saya tahu bahwa saya dapat mendefinisikan pi sendiri sebagai 4 * atan (1) atau acos (1) atau double pi = 3.14. Tentu. Tetapi mengapa pada tahun 2018 saya masih harus melakukannya? Bagaimana cara kerja fungsi matematika standar tanpa pi?

UPD2: Menurut laporan perjalanan ini untuk pertemuan Komite C ++ pada bulan Juli 2019 di Cologne, proposal P0631 (konstanta matematika) diterima ke dalam C ++ 20. Jadi sepertinya pada akhirnya kita akan memiliki nomor pi di perpustakaan standar!

Amomum
sumber
Anda mencatat adanya pertanyaan lama seperti platform pi independen independen Terbaik? . Jika Anda khawatir mereka kedaluwarsa Anda selalu dapat menetapkan hadiah pada salah satu dari mereka yang meminta jawaban berdasarkan C ++ 17 dll ... Maka semua jawaban akan berada di satu tempat. Mengapa masih merupakan pertanyaan yang bagus, tetapi mungkin ini harus fokus pada mengapa dan meminta informasi terkini harus menjadi karunia untuk pertanyaan yang ada.
Shafik Yaghmour
Saya pikir mungkin ada baiknya menambahkan jawaban baru karena C ++ 20 menambahkan konstanta pi sejauh yang saya tahu
Guillaume Racicot
@GuillaumeRacicot saya memperbarui pertanyaan. Tidak yakin apakah kami harus mengatasi C ++ 20 karena belum resmi keluar.
Amomum
@GuillaumeRacicot: Agak terlambat untuk menambahkan satu ...
Davis Herring

Jawaban:

101

Hingga dan termasuk C ++ 17 pi bukan konstanta yang diperkenalkan ke dalam bahasa, dan itu menyakitkan di leher.

Saya beruntung karena saya menggunakan boost dan mereka mendefinisikan pi dengan jumlah desimal yang cukup besar bahkan untuk 128 bit long double.

Jika Anda tidak menggunakan Boost maka hardcode sendiri. Mendefinisikannya dengan fungsi trigonometri memang menggoda, tetapi jika Anda melakukannya, Anda tidak dapat membuatnya constexpr. Keakuratan fungsi trigonometri juga tidak dijamin oleh standar apa pun yang saya tahu ( lih . std::sqrt), Jadi Anda benar-benar berada di tanah berbahaya memang mengandalkan fungsi tersebut.

Ada cara untuk mendapatkan constexprnilai untuk pi menggunakan metaprogramming: lihat http://timmurphy.org/2013/06/27/template-metaprogramming-in-c/


Dari C ++ 20 beberapa kabar baik. Ada adalah sebuah defininition untuk pi . C ++ 20 menambahkan beberapa konstanta matematika di <numbers>. Misalnya std::numbers::piadalah doubletipe.

Referensi: https://en.cppreference.com/w/cpp/numeric/constants

Batsyeba
sumber
9
@Lundin: Tapi itu tidak bisa menjadi constexprsayangnya, itulah sebabnya saya mengatakan "Mendefinisikannya dengan fungsi trigonometri adalah menyusahkan"
Bathsheba
9
Mengapa itu penting? Pi adalah konstanta, tidak dapat berubah atau implementasi spesifik apa pun. Cukup tulis nilai (dalam objek const jika Anda suka) ke jumlah tempat yang signifikan untuk double(atau beberapa nomor konyol jika Anda peduli tentang ganda sangat lama hipotetis).
R .. GitHub BERHENTI MEMBANTU ICE
10
@ R..Masalah yang saya miliki dengan itu adalah apa yang tampak konyol sekarang bisa menjadi sangat waras dalam 20 tahun ke depan. (640k Bill Gates 'muncul dalam pikiran). Saya percaya Boost mengikuti perkembangan arsitektur.
Batsyeba
10
@KonradRudolph: Sebuah presisi tinggi pi hal jika menerapkan berbagai pengurangan. Sebagai contoh, konstanta Pi internal x86 / x87 (mantissa 64-bit penuh) mengarah ke kesalahan terburuk untuk input kecil ke fsininstruksi "sekitar 1,37 trilyun unit di tempat terakhir, meninggalkan kurang dari empat bit yang benar" , dan itu bahkan lebih buruk untuk input besar di mana pengurangan kisaran membungkus beberapa kali. Ini agak tangensial untuk konstanta yang digunakan long doubledalam C ++, tetapi tetap rapi.
Peter Cordes
31

Seperti yang orang lain katakan tidak ada std::pitetapi jika Anda menginginkan PInilai yang tepat Anda dapat menggunakan:

constexpr double pi = std::acos(-1);

Ini mengasumsikan bahwa implementasi C ++ Anda menghasilkan nilai PI yang dibulatkan dengan benar acos(-1.0), yang umum tetapi tidak dijamin .

Tidak constexpr, tetapi dalam praktiknya mengoptimalkan kompiler seperti gcc dan dentang mengevaluasinya pada waktu kompilasi. Menyatakan constpenting bagi pengoptimal untuk melakukan pekerjaan dengan baik.

0x476f72616e
sumber
2
Ini berbahaya karena acos()fungsinya memiliki kemiringan tak terbatas pada x = -1. Akibatnya, metode ini bergantung pada acos()implementasi untuk secara mendasar menangkap kasus -1argumen yang tepat , dan mengembalikan konstanta yang benar secara langsung. Lebih baik menggunakan sesuatu seperti 4*atan(1)yang secara matematis jauh lebih kuat (kemiringan yang berperilaku baik pada x = 1dan perkalian dengan 4 selalu tepat dengan matematika floating point).
cmaster - mengembalikan monica
1
Kami tidak diizinkan untuk menggunakan std::acosdalam ekspresi konstan. dentang melaporkan ini sebagai kesalahan. Harap perhatikan bahwa ini adalah ekstensi yang tidak sesuai dan pada akhirnya harus diperbaiki dalam gcc. Silakan merujuk ke jawaban ini untuk lebih jelasnya.
badola
31

Hingga C ++ 20, tidak, tidak ada standar yang memperkenalkan konstanta yang akan mewakili angka pi (π). Anda dapat memperkirakan angka dalam kode Anda:

constexpr double pi = 3.14159265358979323846;

Bahasa lain seperti C # memiliki konstanta yang dideklarasikan di perpustakaan mereka.

Pembaruan: Dimulai dengan C ++ 20, memang ada pikonstanta yang dideklarasikan di dalam <numbers>header. Hal ini diakses melalui: std::numbers::pi.

Ron
sumber
6
Anda mungkin ingin menambahkan inlineuntuk C ++ 17+.
Deduplicator
8
Ta. Dapatkan upvote, tetapi perhatikan bahwa definisi Anda masih rentan terhadap ketidaktepatan dengan platform dengan definisi yang berbeda double. C # membuatnya mudah karena doublejenisnya sudah diperbaiki. Jika saya berada di komite standar C ++ saya akan mengusulkan sesuatu sepertistd::constants<double>::pi
Bathsheba
11
@Dupuplikator bukan konstexpr secara implisit juga sebaris ...?
whn
4
@R. Cukup adil, meskipun Anda harus menegaskan statis std::numeric_limits<double>::is_iec559;dalam kasus itu. Saya akui, itulah yang saya miliki di "header utama" saya. Perhatikan bahwa secara formal Anda perlu memeriksa semua jenis floating point secara terpisah. Hanya karena satu adalah IEEE754 tidak berarti semuanya.
Batsyeba
2
@DanielSchepler Jadi, apa itu "angka" seharusnya? Saya tidak tahu ada angka ganda dengan basis 16.
BЈовић
29

M_PIdidefinisikan oleh "standar", jika bukan standar bahasa : POSIX dengan ekstensi Antarmuka Sistem X / Terbuka (yang sangat umum didukung dan diperlukan untuk pencitraan merek UNIX resmi).

Ini (masih) tidak pasti apa yang akan ada di C ++ 20, tetapi karena Anda bertanya: mungkin akan memiliki konstanta seperti itu . Makalah ini digabungkan dalam putaran terakhir fitur C ++ 20 (untuk Draft Komite pada Agustus 2019).

Secara khusus, akan ada std::numbers::pi(tipe double) dan templat variabel yang dapat Anda gunakan jika Anda menginginkan jenis titik apung yang berbeda, misalnya std::numbers::pi_v<float>. Daftar konstanta lengkap dapat dilihat di [number.syn] .

Davis Herring
sumber
Jangan ragu untuk mengubah ulang hasil edit saya sesuai keinginan - Saya hanya ingin menambahkan ejaan sebenarnya dari konstanta baru di sini untuk anak cucu.
Barry
12

Ini jelas bukan ide yang baik karena tidak ada tipe yang jelas yang mendefinisikan pi yang berlaku secara universal di seluruh domain.

Pi, tentu saja, bilangan irasional sehingga tidak dapat dengan tepat diwakili oleh tipe C ++ apa pun . Anda mungkin berpendapat bahwa pendekatan alami, oleh karena itu, adalah untuk mendefinisikannya dalam tipe floating point terbesar yang tersedia. Namun, ukuran tipe floating point standar terbesar long doubletidak ditentukan oleh standar C ++ sehingga nilai konstanta akan bervariasi di antara sistem. Lebih buruk lagi, untuk program apa pun yang jenis kerjanya bukan tipe terbesar ini, definisi pi akan menjadi tidak tepat karena akan membebankan biaya kinerja pada setiap penggunaan pi.

Hal ini juga sepele bagi programmer mana pun untuk menemukan nilai pi dan mendefinisikan konstanta mereka sendiri yang cocok untuk digunakan, sehingga tidak memberikan keuntungan besar untuk memasukkannya dalam header matematika.

Jack Aidley
sumber
10
C ++ 14 memberi kami templat variabel. Bukankah ini untuk mereka?
Amomum
21
berbicara seperti anggota komite sejati, berbicara tentang semua cara yang tidak dapat dilakukan meskipun jalannya disajikan dengan jelas. Lihat contoh Template Variabel yang sangat relevan . Saya tidak berpikir jawaban Anda buruk, tapi saya yakin itu tidak akan membantu depresi abadi Bjarne Stroustrup karena penyesalan menyerahkan kendali masa depan C ++ ke komite yang sangat ragu-ragu.
whn
4
@snb Saya setuju bahwa membuat pikonstanta polimorfik adalah jalur yang jelas ke depan - dalam bahasa dengan inferensi tipe Hindley-Milner. Di Haskell, kita selalu memilikinya pi :: Floating a => a, sehingga pisecara otomatis akan memiliki nilai 3.1415927dalam Floatkonteks, 3.141592653589793dalam Doublekonteks, dan πdalam konteks komputasi-simbolik. Tetapi apakah orang benar-benar ingin secara eksplisit membuat parameter template? Tampak agak canggung, terutama jika long doubleimplementasi tetap akan memberikan hasil yang identik di sebagian besar aplikasi.
leftaroundabout
2
@leftaroundabout Saya percaya menulis auto a = pi<float>;benar-benar baik-baik saja, tentu lebih mudah dibaca kemudian terkenal4*atan(1)
Amomum
1
Ugh, saya membatalkan ini dengan salah klik dan sekarang saya tidak bisa membatalkannya. Maaf. Ini layak diberi +1 untuk penjelasan yang cukup bagus tentang alasan komite mengapa tidak ditambahkan, bahkan jika saya pribadi berpikir alasannya secara mendasar cacat karena alasan yang telah ditunjukkan orang.
Dana Gugatan Monica
-1

Diedit - Untuk menghapus istilah yang diperlukan, karena terbukti kontroversial. Ini terlalu banyak istilah absolut.

C ++ adalah bahasa yang besar dan kompleks, untuk alasan itu Komite Standar hanya mencakup hal-hal yang sangat diperlukan . Sebisa mungkin diserahkan ke pustaka standar non-bahasa ... seperti Boost.
boost :: math :: constants

Tiger4Hire
sumber
29
Tentu, std::hermitedan std::cyl_bessel_idan std::coshdan std::mersenne_twister_enginedan std::ranlux48dan std::cauchy_distributiondan std::assoc_laguerredan std::betasemua yang benar-benar diperlukan, kita semua menggunakan mereka setiap hari!
Amomum
2
Saya cukup yakin Anda bahkan tidak bisa membuat komite sendiri menandatangani gagasan bahwa semua yang mereka pilih adalah "mutlak perlu". Ini bukan tentang perlunya tetapi tentang menambahkan nilai ke bahasa - hampir tidak ada di perpustakaan standar yang diperlukan untuk menulis program, dan dalam hal ini sebagian besar bahasa inti dapat dibuang juga (jika Anda tidak keberatan bekerja di sebuah Turing tarpit, artinya). Nilai dimasukkannya konstanta untuk pi dapat diperdebatkan, tetapi gagasan bahwa itu tidak ada di sana karena itu hanya tidak perlu tidak menahan air.
Jeroen Mostert
Jangan mengeluh pada saya, saya hanya mengutip komite standar. Ada diskusi terbuka panjang tentang berapa banyak dorongan harus dimasukkan dalam standar C ++ 11. Alasan mengapa subset sekecil itu membuatnya karena penulis kompiler mengeluh tentang berapa banyak pengujian yang terlibat. Oleh karena itu, jika ada sesuatu dalam standar, itu ada karena seseorang menganggap perlu untuk membakukannya. Hanya karena Anda tidak tahu mengapa, bukan berarti tidak ada alasan.
Tiger4Hire
1
@ Tiger4Hire Saya yakin ada alasan untuk semuanya, saya tidak bisa mengerti mengapa konstanta untuk pi tidak ditambahkan ketika banyak hal yang lebih kompleks. Constant mudah ditulis dengan templat variabel dan tidak akan memerlukan banyak pengujian dari penulis kompiler.
Amomum
@ Amomum: Ya, menambahkan pi sepertinya overhead kecil untuk kemenangan besar. Pikiran, secara pribadi saya lebih suka melihat std :: network sebelum std :: math :: constants.
Tiger4Hire