P ++ sabiti C ++ ile nasıl kullanılır


476

Bazı C ++ programında PI sabiti ve trigonometrik fonksiyonları kullanmak istiyorum. Trigonometrik fonksiyonları ile alıyorum include <math.h>. Ancak, bu başlık dosyasında PI için bir tanım yok gibi görünüyor.

Manuel olarak tanımlamaksızın PI'yi nasıl alabilirim?


3
@tiwo, sen arasındaki fark ne soruyorsun 3.14, 3.141592ve atan(1) * 4?
Nikola Malešević

21
Bir yan not olarak, cmath C için math.h yerine C ++ 'da kullanılmalıdır.
juzzlin

4
Gevşek olarak ilgili: PI değerinin doğrudan tanımdan nasıl hesaplanacağı hakkında cise.ufl.edu/~manuel/obfuscate/pi.c adresine bakınız .
lorro

Yanıtlar:


537

Bazı (özellikle eski) platformlarda (aşağıdaki yorumlara bakın)

#define _USE_MATH_DEFINES

ve sonra gerekli başlık dosyasını ekleyin:

#include <math.h>

ve pi değerine şu yolla erişilebilir:

M_PI

In my math.h(2014) bu gibi tanımlanır:

# define M_PI           3.14159265358979323846  /* pi */

ama math.hdaha fazlası için kontrol edin . "Eski" den bir alıntı math.h(2009 yılında):

/* 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.
 */

Ancak:

  1. daha yeni platformlarda (en azından 64 bit Ubuntu 14.04'ümde) _USE_MATH_DEFINES

  2. (Son zamanlarda) Linux platformlarında long doublebir GNU Uzantısı olarak da sağlanan değerler vardır :

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */

51
#define _USE_MATH_DEFINESardından görsel c ++ 'da #include <math.h>tanımlar M_PI. Teşekkürler.
Etan

3
Cygwin üstbilgileriyle de çalışır.
Rob

24
Bunun cmathyerine her zaman dahil edebilirsiniz math.h.
Richard J. Ross III

10
_USE_MATH_DEFINESGCC'nin şikayet edip etmediğini tanımladıktan sonra bile __STRICT_ANSI__, tanımlanmasına izin verilmeyen tanımlanmış (belki de geçtiniz -pedanticveya -std=c++11) M_PI, dolayısıyla tanımını kaldırın -D__STRICT_ANSI__. Kendiniz tanımlarken, bir makro yerine C ++ olduğu için yapmalısınız constexpr auto M_PI = 3.14159265358979323846;.
legends2k

1
2018 itibariyle, cevap <math.h> yerine <cmath> kullanacak şekilde güncellenmelidir
jaskmar

170

Pi olarak hesaplanabilir atan(1)*4. Değeri bu şekilde hesaplayabilir ve önbelleğe alabilirsiniz.


78
C ++ 11 kullanıcıları için:constexpr double pi() { return std::atan(1)*4; }
matiu

41
-1: Yalnızca atan(1)*4 == 3.141592653589793238462643383279502884(kabaca konuşursak) çalışır. Ben bahse girmezdim. Normal olun ve sabiti tanımlamak için ham bir harf kullanın. İhtiyacınız olmadığında neden hassasiyet kaybedersiniz?
Thomas Eding

29
Bir ile çarpma işlemi önleyebilirsiniz atan2(0, -1);.
efsane2k

44
@matiu atandeğil constexpr.
R. Martinho Fernandes

45
acos(-1)Bunun yerine deneyin , gerek yok atan2.
user541686

113

Ayrıca, istenen matematik türü için (örneğin float ve double) maksimum doğrulukla önemli matematik sabitlerini tanımlayan boost kullanabilirsiniz.

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

Check out boost belgelerine Daha fazla örnek için.


184
Boost: 1999'dan beri C + 'nın zaten gereksiz karmaşıklığını artırmak!
Dan Moulding

47
Akılda kalıcı ve kısmen doğru. Öte yandan, destek zaman zaman olağanüstü derecede yararlı olabilir ...
BuschnicK

59
@DanMoulding: Uhm. C bildiğin diğer dil mi? Çünkü C dışında bildiğim diğer tüm diller, C ++ 'dan daha büyük standart kütüphaneye sahiptir (örneğin Python, Haskell, C #, PHP, Delphi, Erlang, Java, ......). Kişisel deneyimlerden, bu seçkin not gonna use libs-opinion bir haşere ve muhtemelen C ++ ile yazılmış kötü yazılım için bir numaralı nedendir.
Sebastian Mach

11
@Gracchus: Evet. Kütüphaneler olmadan (veya yeni C ++ 11 kütüphaneleri olmadan) C ++, bu dili sevdiğim kadar ve her şeyi kendim kodlamak istediğim kadar çok üretken değil.
Sebastian Mach

14
İnanıyorum ki karmaşıklık boyut değil . Muhtemelen a) 3 iç içe isim alanına ve b) pi'yi normal bir sabitten ziyade geçici bir fonksiyon olarak tanımlamak.
Timmmm

83

Bunun yerine çip üzerindeki FPU biriminden alın:

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

double PI = get_PI();

40
:-) muhtemelen bu platform bağımsız değil, ama güzel bir ek egzotik çözüm!
Etan

3
Burada kutunun dışında nasıl olsa seviyorum;)
VivienLeger

1
Bu cevabı seviyorum. Özellikle, iyileştirici derleyicilerin modern platformlar kadar korkunç bir şekilde yer almadığı, küçük bir heves olan eski x86 platformlarını hedeflerken yararlıdır. Bu Henrik için teşekkürler!
Matt

49

İhtiyacınız olan hassasiyete sadece pi yazmanızı tavsiye ederim. Bu, yürütmenize hiçbir hesaplama süresi eklemez ve üstbilgi veya # tanım kullanmadan taşınabilir olur. Acos veya atan hesaplamak her zaman önceden hesaplanmış bir değer kullanmaktan daha pahalıdır.

const double PI  =3.141592653589793238463;
const float  PI_F=3.14159265358979f;

28
Bu, bu yaklaşımı benim almamamız için harika bir örnektir, biz insanlar hatalar, yuvarlama, kopyalama ve yapıştırma vb. Yapıyoruz. Bence M_PI kullanmak doğru yaklaşım.
nacho4d

10
Biri bunu C ++ 11'de yapıyorsa, consta constexpr.
legends2k

3
@ nacho4d Varsa M_PI'yi de tercih ederim, ancak tüm sistemler POSIX uyumlu değildir. Bu yaklaşımın M_PI'nin mevcut olmadığı durumlarda 4 * atan (1) yönteminden daha iyi olduğunu düşünüyorum.
m24p

2
"Acos veya atan hesaplamak her zaman daha pahalıdır" doğru değildir. Herhangi bir modern optimizasyon derleyici standart matematik fonksiyonları hakkında her şeyi bilir ve bunlar aracılığıyla sürekli yayılabilir. Bkz. Örneğin goo.gl/BvdJyr
Nemo

2
@Nemo, Counter örneği: godbolt.org/g/DsAern Başka bir yerde söylendiği gibi, şu anda sadece GCC bunu yapıyor ve muhtemelen temel matematik işlevlerini bildirdiği için görünüyor constexpr.
Parker Coates

47

Yazmaktan ziyade

#define _USE_MATH_DEFINES

Derleyicinizi kullanmanızı -D_USE_MATH_DEFINESveya /D_USE_MATH_DEFINESderleyicinize bağlı olarak öneririm .

Bu şekilde, üstbilgiyi sizden önce (ve #define içermeyen) içeren bir durumda bile, izlemeniz gereken yaşları alacağınız belirsiz bir derleyici hatası yerine sabitlere sahip olacağınızdan emin olabilirsiniz.


İyi bahşiş. Eğer "siz" bir derleme birimiyse, elbette bir şey dahil edilmeden önce makronun tanımlandığından emin olabilirsiniz. Ancak "siz" bir başlık dosyasıysanız, sizin kontrolünüz dışındadır.
Steve Jessop

3
Aslında "siz" bir derleme birimi olsa bile ... başlıkların sırasına bağlı olarak bakım kabusu için en kısa yol ...
Matthieu M.

1
Yine de başlıkların sırasına bağlı olmak zorunda değilsiniz. #Define'ı herhangi bir şey eklemeden önce (en azından # hiçbir şeyin bunu yapmadıklarını varsayarak) yapmanız şartıyla, başlıkların birbirini içermesi önemli değildir. Aynı şey NDEBUG için de geçerlidir.
Steve Jessop

1
Bir projedeki en yaygın sorun, örneğin Visual Studio ile derliyorsanız, derleyicinin dosyalarınızı hangi sırayla geçeceğini bilmediğinizden <cmath>, farklı yerlerde kullanırsanız büyük bir acı olur (özellikle dahil ettiğiniz başka bir kütüphane tarafından dahil edilmişse). Eğer o kısmı başlık muhafızlarının dışına koyarlarsa çok daha iyi olurdu ama şimdi bu konuda pek bir şey yapamazlar. Derleyici direktifi gerçekten iyi çalışıyor.
meneldal

40

Resmi standart kütüphane sabit bir PI tanımlamadığı için, onu kendiniz tanımlamanız gerekir. Peki "PI'yi manuel olarak tanımlamadan nasıl alabilirim?" "Yapmazsınız - veya derleyiciye özgü bazı uzantılara güvenirsiniz". Taşınabilirlikle ilgili endişeleriniz yoksa, derleyici kılavuzunuzu kontrol edebilirsiniz.

C ++ yazmanıza izin verir

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

ancak bu sabitin başlatılmasının statik olduğu garanti edilmez. Ancak G ++ derleyicisi bu matematik işlevlerini içsel olarak işler ve bu sabit ifadeyi derleme zamanında hesaplayabilir.


6
Genellikle acos (-1) kullanıyorum, dediğin gibi derleme zamanı değerlendiriliyor. M_PI, acos (-1) ve atanan (1) * 4'ü test ettiğimde, aynı değerler aldım.
Micah

2
Geleneksel yöntem kullanmaktır 4*atan(1.): atanuygulaması kolaydır ve 4 ile çarpılması kesin bir işlemdir. Elbette, modern derleyiciler tüm sabitleri gerekli hassasiyetle katlar (katlamayı hedefler) ve Euler'in formülünün tersi olan acos(-1)ve hatta std::abs(std::arg(std::complex<double>(-1.,0.)))göründüğünden daha estetik olarak hoş olanın kullanılması oldukça mantıklıdır (ekledim absçünkü t Karmaşık düzlemin nasıl kesildiğini veya hiç tanımlanmadığını hatırlayın).
tobi_s

Hiç kimse yanlışlıkla ciddi olduğunuzu düşünmez (yine -_- '). Bu korkunç bir çözüm. Atanan uygulama, uygulanması anlamına gelen standart tarafından tanımlanmaz ve muhtemelen hw bağımlıdır. Bu, sayısalların korkunç olabileceği anlamına gelir, yani genel olarak 3.14'ü kullanarak daha iyi olabilirsiniz. Ayrıca, özel durumlar için bile oldukça yavaş.
midjji

32

Gönderen math.h ait Posix adam sayfası :

   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
Güzel cevap ama bağlantı öldü. Önerim bu bir yerine.
Abderrahim Kitouni

30

C ++ 20 std::numbers::pi

Sonunda geldi: http://eel.is/c++draft/numbers

Kullanımın şöyle olmasını bekliyorum:

#include <numbers>
#include <iostream>

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

Destek GCC'ye geldiğinde bunu deneyeceğim, GCC 9.1.0 g++-9 -std=c++2a hala desteklemiyor.

Kabul edilen teklif şunları açıklar:

5.0. “Üstbilgiler” [üstbilgiler] Tabloda [sekme: cpp.library.headers], yeni<math> üstbilgi eklenmesi gerekir.

[...]

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

std::numbers::eElbette bir de var :-) Nasıl Euler sabit veya C + + powered Euler hesaplamak için?

Bu sabitler C ++ 14 değişken şablon özelliğini kullanır: C ++ 14 Değişken Şablonlar: amaçları nedir? Herhangi bir kullanım örneği var mı?

Taslağın daha önceki sürümlerinde, sabit şunun altındaydı std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf


27

Standart C ++ 'nın PI için bir sabiti yoktur.

Birçok C ++ derleyicisi M_PI, cmath(veya math.hC için) standart olmayan bir uzantı olarak tanımlar . Bunu #define _USE_MATH_DEFINESgörmeden önce yapmanız gerekebilir .


18

yapardım

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

veya

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

Ben ediyorum değil ihtiyacınız hassas tt yazarak . Bunun anlamı ne? İhtiyacınız hassas hassas olduğunu T, ama biz hakkında hiçbir şey bilmiyoruzT .

Şunu söyleyebilirsiniz: Neden bahsediyorsun? Tolacak float, doubleya da long double. Yani, sadece hassasiyetini yazın long double, yani

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

Ama gelecekte standartta yeni bir kayan nokta türü olmayacağını gerçekten biliyor musunuz? long double ? Yapmazsın.

İşte bu yüzden ilk çözüm çok güzel. Standardın yeni bir tip için trigonometrik fonksiyonları aşırı yükleyeceğinden emin olabilirsiniz.

Ve lütfen, başlangıçta trigonometrik bir fonksiyonun değerlendirilmesinin performans cezası olduğunu söyleme.


1
Herkes arg(log(x)) == πiçin unutmayın 0 < x < 1.
0xbadf00d

Bu korkunç bir fikir. tür başına aşırı yüklenmiş şablon constexpr kullanın, böylece yeni bir tür görünürse onu tanımlamaya zorlamak için bir derleme hatası alırsınız. Trig türleri kayan nokta tipleri ile sınırlı olmadığı için genellikle korkunçtur. Böylece atanan (1) hatanın tadını çıkarın ... Standart, trigonometrik fonksiyonların gerçek trigonometrik değerlerini türün doğruluğuna göre hesapladığını garanti etmez. Genellikle yapmazlar ve örneğin fastmath ile daha da kötüleşir ve her zaman özel değerler için özellikle kötüdür.
midjji

10

Tüm üsleri kapsayan projedeki ortak başlığımdan birinde aşağıdakileri kullanıyorum:

#define _USE_MATH_DEFINES
#include <cmath>

#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

#ifndef M_PIl
#define M_PIl (3.14159265358979323846264338327950288)
#endif

Yan notta, aşağıdaki derleyicilerin tümü eklerseniz M_PI ve M_PIl sabitlerini tanımlar <cmath>. Yalnızca VC ++ için gerekli olan #define _USE_MATH_DEFINES eklemenize gerek yoktur.

x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+

Downvoter bu cevapta neyin yanlış olduğu hakkında yorum yapabilir mi? Bu iyi araştırılmış ve test edilmiş ve gerçek sistemde kullanılmaktadır. Bir şeylerin yanlış olması durumunda bunu geliştirmek isterdim.
Shital Shah

1
FYI, Borland C ++ derleyicileri de M_PIihtiyaç duyulmadan tanımlanır_USE_MATH_DEFINES
Remy Lebeau

8

Genelde kendimi tanımlamayı tercih ederim: const double PI = 2*acos(0.0); çünkü tüm uygulamalar sizin için bunu sağlamaz.

Bu işlevin çalışma zamanında çağrılıp çağrılmadığı veya derleme zamanında statik olup olmadığı sorusu genellikle sorun değildir, çünkü yine de yalnızca bir kez olur.


8
acos (-1) de pi'dir.
Roderick Taylor

3
Anında işlenen yüklemek, bir işleneni bellek konumundan okumaktan daha az CPU talimatları ve / veya daha az gecikme süresine sahiptir. Ayrıca, sadece derleme zamanında bilinen ifadeler önceden hesaplanabilir (yani double x = pi * 1.5;ve benzerleri). Sıkı döngülerde gevrek matematikte PI kullanmayı planlıyorsanız, değerin derleyici tarafından bilindiğinden emin olmalısınız.
Eugene Ryabtsev

7

Bu makaleye C ++ 14 ve üstü için harika bir ipucu olan Danny Kalev tarafından rastladım .

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

Özellikle şablonlar türe göre kullanabildiğinden, bunun oldukça havalı olduğunu düşündüm (orada yapabileceğim en yüksek hassasiyetli PI'yi kullanacağım).

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 vb. Değerler standart C ++ değildir, bu nedenle bir constexpr daha iyi bir çözüm gibi görünür. Aynı pi'yi hesaplayan farklı const ifadeleri formüle edilebilir ve bana (hepsi) bana tam doğruluğu sağlayıp sağlamadıklarını ilgilendirir. C ++ standardı pi'nin nasıl hesaplanacağından açıkça bahsetmez. Bu nedenle, pi'yi manuel olarak tanımlamaya geri dönme eğilimindeyim. Aşağıda tüm pi kesirlerini tam doğrulukla destekleyen çözümü paylaşmak istiyorum.

#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
Çok hoş. Bu sayının sonunda bir "l" ya da "L" olması gerekebilir. Linux üzerinde derleyici gcc'den daralma uyarısı alıyorum.
Grant Rostig

2

Pencerelerin (cygwin + g ++) üzerinde, bunun gerekli bayrağı eklemek buldum -D_XOPEN_SOURCE=500tanımını işlemek önişlemcisinden için M_PIde math.h.


2
Bu bir cevap değil, fritzone'un cevabına bir yorum.
0xbadf00d

2
@ 0xbadf00d: M_PIBelirli bir platformda çalışmak için gereken adımları sağlayan tamamen bağımsız bir cevaptır . Bu, başka bir platformun cevabı hakkında bir yorum değil, başka bir platform için bir cevabın bu bir yorum olduğu.
Ben Voigt

2

C ++ 14 yapmanıza izin verir static constexpr auto pi = acos(-1);


9
std::acosbir değil constexpr. Yani, kodunuz derlenmeyecek.
0xbadf00d

@ 0xbadf00d Bunu g ++ ile derledim
Willy Goat

12
@WillyGoat: O g ++, çünkü yanlış acosdeğil constexprC ++ 14 ve olmak için öne sürülmemektedir constexprC bile ++ 17
Ben Voigt

@BenVoigt herhangi bir matematik işlevi var constexprmı? Görünüşe göre değil: stackoverflow.com/questions/17347935/constexpr-math-functions
wcochran

1
@wcochran: Birçok yeni matematik işlevi vardır constexpr, örneğin bakınız ( github.com/kthohr/gcem ). Ancak aynı addaki C işlevleriyle geriye dönük uyumlu değildirler, bu nedenle eski adları devralamazlar.
Ben Voigt

2

Bazı zarif çözümler. Trigonometrik fonksiyonların hassasiyetinin, tiplerin hassasiyetine eşit olduğundan şüpheliyim. Sabit bir değer yazmayı tercih edenler için, bu g ++ için çalışır: -

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

Gelecekteki uzun uzun uzun çift tip için 256 ondalık basamak doğruluğu yeterli olmalıdır. Daha fazlası gerekirse https://www.piday.org/million/ adresini ziyaret edin .



1

Bunu yapabilirsiniz:

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

Eğer M_PIzaten tanımlanmış cmath, bu dahil daha başka bir şey yapmayacağım cmath. Eğer M_PI(Visual Studio, örneğin durumda) tanımlanmamıştır, onu tanımlayacaktır. Her iki durumda da,M_PI pi değerini almak için .

Bu pi değeri Qt Creator'ın qmath.h dosyasından gelir.


1

Bunu kullanabilirsiniz:

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

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

Matematik Sabitler Standart C / C ++ 'da tanımlanmamıştır. Bunları kullanmak için, önce veya öğelerini tanımlamanız _USE_MATH_DEFINESve sonra eklemeniz gerekir .cmathmath.h

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.