C ++ 11, 14, 17 หรือ 20 มีค่าคงที่มาตรฐานสำหรับ pi หรือไม่?


170

มีปัญหาค่อนข้างโง่กับหมายเลข pi ใน C และ C ++ เท่าที่ฉันรู้M_PIกำหนดไว้math.hไม่จำเป็นต้องมีมาตรฐาน

มาตรฐาน C ++ ใหม่แนะนำคณิตศาสตร์ที่ซับซ้อนจำนวนมากในไลบรารีมาตรฐาน - ฟังก์ชันไฮเพอร์โบลิกstd::hermiteและstd::cyl_bessel_iเครื่องกำเนิดเลขสุ่มที่แตกต่างกันเป็นต้น

มาตรฐาน 'ใหม่' มีค่าคงที่สำหรับ pi หรือไม่? ถ้าไม่ - ทำไม คณิตศาสตร์ที่ซับซ้อนทั้งหมดนี้ทำงานอย่างไรหากปราศจากมัน?

ฉันตระหนักถึงคำถามที่คล้ายกันเกี่ยวกับ pi ใน C ++ (เป็นเวลาหลายปีและมาตรฐานเก่า); ฉันต้องการทราบสถานะปัจจุบันของปัญหา

ฉันสนใจด้วยเช่นกันว่าทำไมทำไม C ++ ถึงไม่มีค่าคงที่ pi แต่มีคณิตศาสตร์ที่ซับซ้อนกว่าเยอะ

UPD: ฉันรู้ว่าฉันสามารถกำหนด pi ตัวเองเป็น 4 * atan (1) หรือ acos (1) หรือ double pi = 3.14 แน่ใจ แต่ทำไมในปี 2561 ฉันยังต้องทำอีก ฟังก์ชั่นคณิตศาสตร์มาตรฐานทำงานอย่างไรโดยไม่ต้อง pi?

UPD2: ตามรายงานการเดินทางครั้งนี้สำหรับการประชุมคณะกรรมการ C ++ ในเดือนกรกฎาคม 2019 ในโคโลญข้อเสนอ P0631 (ค่าคงที่ทางคณิตศาสตร์) ได้รับการยอมรับใน C ++ 20 ดูเหมือนว่าในที่สุดเราจะมีหมายเลข pi ในไลบรารีมาตรฐาน!


คุณทราบการมีอยู่ของคำถามเก่า ๆ เช่นแพลตฟอร์มที่ดีที่สุดคงที่ pi อิสระหรือไม่ . หากคุณกังวลว่าพวกเขาล้าสมัยคุณสามารถตั้งค่าเงินรางวัลให้กับหนึ่งในนั้นเพื่อขอคำตอบจาก C ++ 17 ฯลฯ ... จากนั้นคำตอบทั้งหมดจะอยู่ในที่เดียว ทำไมยังคงเป็นคำถามที่ดี แต่บางทีสิ่งนี้ควรมุ่งเน้นไปที่สาเหตุและการขอให้ทันสมัยควรเป็นคำถามที่มีอยู่มากมาย
Shafik Yaghmour

ฉันคิดว่ามันอาจจะคุ้มค่าที่จะเพิ่มคำตอบใหม่ตั้งแต่ C ++ 20 เพิ่มค่า pi เท่าที่ฉันรู้
Guillaume Racicot

@GuillaumeRacicot ฉันอัปเดตคำถาม ไม่แน่ใจว่าเราควรระบุที่อยู่ C ++ 20 หรือไม่เนื่องจากยังไม่เป็นทางการ
Amomum

@ GuillaumeRacicot: มันสายไปหน่อย…
Davis Herring

คำตอบ:


101

มากถึงและรวมถึง C ++ 17 piไม่ใช่ค่าคงที่ที่นำมาใช้กับภาษาและเป็นอาการปวดคอ

ผมโชคดีในการที่ผมใช้งานที่เพิ่มขึ้นและพวกเขากำหนดปี่long doubleที่มีจำนวนมากพอสำหรับตำแหน่งทศนิยมแม้แต่นิด 128

หากคุณไม่ใช้ Boost ให้ทำการ hardcode ด้วยตนเอง กำหนดด้วยฟังก์ชั่นตรีโกณมิติเป็นที่ดึงดูด constexprแต่ถ้าคุณทำเช่นนั้นคุณจะไม่สามารถทำให้มันเป็น ความถูกต้องของฟังก์ชันตรีโกณมิตินี้ยังไม่รับประกันใด ๆ ฉันรู้ว่ามาตรฐาน ( CF . std::sqrt) ดังนั้นจริงๆคุณอยู่บนพื้นดินที่เป็นอันตรายแน่นอนอาศัยเช่นฟังก์ชั่น

มีวิธีรับconstexprค่าpiโดยใช้ metaprogramming: ดูที่http://timmurphy.org/2013/06/27/template-metaprogramming-in-c/


จาก C ++ 20 ข่าวดี มีเป็น defininition สำหรับปี่ C ++ 20 <numbers>เพิ่มค่าคงที่ทางคณิตศาสตร์บางอย่างใน ยกตัวอย่างเช่นstd::numbers::piเป็นdoubleประเภท

การอ้างอิง: https://en.cppreference.com/w/cpp/numeric/constants


9
@Lundin: แต่นั่นไม่constexprน่าเสียดายซึ่งเป็นเหตุผลที่ฉันพูดว่า "การกำหนดด้วยฟังก์ชั่นตรีโกณมิติเป็นความเจ็บปวด"
Bathsheba

9
ทำไมมันถึงสำคัญ? Pi เป็นค่าคงที่ไม่มีการเปลี่ยนแปลงหรือสิ่งใด ๆ ในการนำไปปฏิบัติโดยเฉพาะ เพียงแค่เขียนค่า (ในวัตถุ const ถ้าคุณต้องการ) ไปยังจำนวนสถานที่ที่สำคัญสำหรับdouble(หรือบางจำนวนไร้สาระถ้าคุณสนใจเกี่ยวกับคู่ยาวจริงๆสมมุติยาว)
.. GitHub หยุดช่วยน้ำแข็ง

10
@ R .. ปัญหาที่ฉันมีนั่นคือสิ่งที่ดูเหมือนไร้สาระในขณะนี้สามารถกลายเป็นสติสมบูรณ์ในเวลา 20 ปีหรือดังนั้น (น้ำพุแห่งความจริง 640k ของ Bill Gates) ฉันเชื่อใจ Boost เพื่อให้ทันกับวิวัฒนาการสถาปัตยกรรม
Bathsheba


10
@ KonradRudolph: ไพที่มีความแม่นยำสูงมีความสำคัญหากใช้การลดช่วง ตัวอย่างเช่นค่าคงตัว Pi ภายในของ x86 / x87 (mantissa 64- บิตเต็ม) นำไปสู่ข้อผิดพลาดกรณีเลวร้ายที่สุดสำหรับอินพุตขนาดเล็กเพื่อการfsinเรียนการสอนของ "ประมาณ 1.37 ล้านหน่วยในสถานที่สุดท้ายทิ้งน้อยกว่าสี่บิตที่ถูกต้อง"และ ยิ่งแย่ลงสำหรับอินพุตขนาดใหญ่ที่การลดช่วงล้อมรอบหลายครั้ง นี่ค่อนข้างจะเป็นวงสัมผัสกับค่าคงที่ที่ใช้สำหรับlong doubleใน C ++ แต่ยังเรียบร้อยอยู่ดี
Peter Cordes

31

อย่างที่คนอื่นพูดว่าไม่มีstd::piแต่ถ้าคุณต้องการPIคุณค่าที่แม่นยำคุณสามารถใช้:

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

นี้อนุมานว่าคุณ C ++ ดำเนินการผลิตที่คุ้มค่าอย่างถูกต้องในชีวิตประจำวันของ PI จากacos(-1.0), ซึ่งเป็นเรื่องธรรมดา แต่ไม่รับประกัน

ไม่ใช่constexprแต่ในทางปฏิบัติการปรับแต่งคอมไพเลอร์อย่าง gcc และ clang ประเมินมันในเวลารวบรวม แม้ว่าการประกาศconstเป็นสิ่งสำคัญสำหรับเครื่องมือเพิ่มประสิทธิภาพในการทำงานที่ดี


2
นี้เป็นอันตรายเพราะฟังก์ชั่นที่มีความลาดชันที่ไม่มีที่สิ้นสุดacos() x = -1ดังนั้นวิธีนี้ขึ้นอยู่กับการacos()ใช้งานโดยทั่วไปจะจับกรณีของการ-1โต้แย้งที่แม่นยำและคืนค่าคงที่ที่ถูกต้องโดยตรง ใช้สิ่งที่ดีกว่า4*atan(1)ซึ่งมีความแข็งแกร่งทางคณิตศาสตร์มากขึ้น (ความลาดชันที่มีพฤติกรรมดีx = 1และการคูณด้วย 4 นั้นแม่นยำเสมอกับคณิตศาสตร์จุดลอยตัว)
cmaster - คืนสถานะโมนิกา

1
เราไม่ได้รับอนุญาตให้ใช้std::acosในการแสดงออกอย่างต่อเนื่อง เสียงดังกราวรายงานว่านี่เป็นข้อผิดพลาด โปรดทราบว่านี่เป็นส่วนขยายที่ไม่สอดคล้องและควรแก้ไขเป็น gcc ในที่สุด โปรดดูคำตอบนี้สำหรับรายละเอียดเพิ่มเติม
badola

31

สูงถึง C ++ 20, ไม่, ไม่มีมาตรฐานใดนำค่าคงที่มาใช้แทนค่าตัวเลข pi (π) คุณสามารถประมาณจำนวนในรหัสของคุณ:

constexpr double pi = 3.14159265358979323846;

ภาษาอื่นเช่น C # มีค่าคงที่ที่ประกาศในไลบรารี

อัปเดต: เริ่มต้นด้วย C ++ 20 มีการpiประกาศค่าคงที่ภายใน<numbers>ส่วนหัวอย่างแน่นอน สามารถเข้าถึงได้ผ่าน: std::numbers::pi.


6
คุณอาจต้องการเพิ่มinlineสำหรับ C ++ 17 +
Deduplicator

8
ขอบคุณ มี upvote doubleแต่ทราบว่าคำนิยามของคุณยังคงความเสี่ยงที่จะไม่แน่ชัดกับแพลตฟอร์มที่มีคำจำกัดความที่แตกต่างกันของ C # ใช้งานง่ายเนื่องจากdoubleประเภทได้รับการแก้ไข ถ้าฉันอยู่ในคณะกรรมการมาตรฐาน C ++ ฉันขอเสนอบางอย่างเช่นstd::constants<double>::pi
Bathsheba

11
@Dupuplicator ไม่ใช่ constexpr แบบอินไลน์เช่นกัน ... ?
WHN

4
@R ยุติธรรมเพียงพอแม้ว่าคุณควรจะยืนยันstd::numeric_limits<double>::is_iec559;ในกรณีนั้น ซึ่งฉันยอมรับว่าเป็นสิ่งที่ฉันมีใน "ส่วนหัวหลัก" ของฉัน โปรดทราบว่าอย่างเป็นทางการคุณต้องตรวจสอบทุกประเภทจุดลอยแยกต่างหาก เพียงเพราะหนึ่งคือ IEEE754 ไม่ได้หมายความว่าพวกเขาทั้งหมดเป็น
Bathsheba

2
@DanielSchepler แล้ว "number" นั่นคืออะไร? ฉันไม่รู้ว่ามีตัวเลขสองตัวที่มีฐาน 16 ตัว
BЈовић

29

M_PIถูกกำหนดโดย "มาตรฐาน" หากไม่ใช่มาตรฐานภาษา : POSIXมีส่วนขยาย X / Open System Interfaces (ซึ่งได้รับการสนับสนุนอย่างมากและจำเป็นสำหรับการสร้างแบรนด์ UNIX อย่างเป็นทางการ)

มันยังไม่แน่ใจว่าจะเป็นอย่างไรใน C ++ 20 แต่เนื่องจากคุณถามว่า: มันอาจจะมีค่าคงที่เช่นนั้น กระดาษถูกรวมในรอบสุดท้ายของคุณสมบัติ C ++ 20 (สำหรับร่างคณะกรรมการในเดือนสิงหาคม 2019)

โดยเฉพาะมีทั้งสองจะstd::numbers::pi(ประเภทdouble) std::numbers::pi_v<float>และแม่แบบตัวแปรที่คุณสามารถใช้ถ้าคุณต้องการที่แตกต่างกันชนิดลอยจุดเช่น รายการเต็มรูปแบบของค่าคงที่สามารถเห็นใน[numbers.syn]


อย่าลังเลที่จะแก้ไขคำพูดของฉันตามที่เห็นสมควร - ฉันแค่ต้องการเพิ่มการสะกดที่แท้จริงของค่าคงที่ใหม่ที่นี่เพื่อลูกหลาน
Barry

12

เห็นได้ชัดว่าไม่ใช่ความคิดที่ดีเพราะไม่มีประเภทที่ชัดเจนซึ่งกำหนด pi ที่สามารถใช้งานได้ทั่วทั้งโดเมน

Pi เป็นของหลักสูตรเป็นจำนวนที่ไม่ลงตัวจึงไม่สามารถแสดงได้อย่างถูกต้องโดยการใด ๆ c ++ ประเภท คุณอาจยืนยันว่าวิธีธรรมชาตินั้นคือการกำหนดในประเภทจุดลอยที่ใหญ่ที่สุดที่มีอยู่ อย่างไรก็ตามขนาดของชนิดจุดลอยมาตรฐานที่ใหญ่ที่สุดlong doubleไม่ได้กำหนดโดยมาตรฐาน C ++ ดังนั้นค่าของค่าคงที่จะแตกต่างกันระหว่างระบบ ที่แย่กว่านั้นสำหรับโปรแกรมใด ๆ ที่ประเภทการทำงานไม่ใช่แบบที่ใหญ่ที่สุดคำจำกัดความของ pi จะไม่เหมาะสมเนื่องจากมันจะกำหนดต้นทุนด้านประสิทธิภาพสำหรับการใช้ pi ทุกครั้ง

มันเป็นเรื่องธรรมดาสำหรับโปรแกรมเมอร์ที่จะหาค่าของ pi และกำหนดค่าคงที่ที่เหมาะสมสำหรับการใช้งานของตัวเองดังนั้นจึงไม่ได้ให้ประโยชน์อย่างมากในการรวมไว้ในส่วนหัวของคณิตศาสตร์


10
C ++ 14 ให้แม่แบบตัวแปร มันไม่ใช่สิ่งที่พวกเขามีไว้เพื่อ?
Amomum

21
พูดเหมือนสมาชิกคณะกรรมการที่แท้จริงพูดคุยเกี่ยวกับวิธีที่ไม่สามารถทำได้แม้จะมีการนำเสนอที่ชัดเจน ดูแม่แบบเช่นตัวแปรที่เกี่ยวข้องตื่นตาตื่นใจ ฉันไม่คิดว่าคำตอบของคุณไม่ดี แต่ฉันแน่ใจว่ามันจะไม่ช่วย Bjarne Stroustrup ของความเศร้าใจในการควบคุมอนาคตของ C ++ ต่อคณะกรรมการเด็ดขาด
WHN

4
@snb ฉันเห็นด้วยที่ทำ piค่าคงที่แบบ polymorphic นั้นเป็นเส้นทางที่ชัดเจนไปข้างหน้า - ในภาษาที่มีการอนุมานแบบ Hindley-Milner ใน Haskell เรามีอยู่เสมอpi :: Floating a => aเพื่อที่piจะมีค่าโดยอัตโนมัติ3.1415927ในFloatบริบท3.141592653589793ในDoubleบริบทและπในบริบทการคำนวณเชิงสัญลักษณ์ แต่คนต้องการที่จะยกตัวอย่างพารามิเตอร์แม่แบบอย่างชัดเจน? ดูเหมือนจะอึดอัดใจโดยเฉพาะอย่างยิ่งหากการlong doubleใช้งานแบบคงที่จะให้ผลลัพธ์ที่เหมือนกันในแอพพลิเคชันส่วนใหญ่
leftaroundabout

2
@leftaroundabout ฉันเชื่อว่าการเขียนauto a = pi<float>;นั้นสมบูรณ์ดีแน่นอนสามารถอ่านได้มากขึ้นและฉาวโฉ่มากขึ้น4*atan(1)
Amomum

1
ฮึฉันลงคะแนนนี่โดย misclick และตอนนี้ฉันไม่สามารถยกเลิกได้ ขอโทษ สิ่งนี้สมควรได้รับ +1 สำหรับคำอธิบายที่ดีเกี่ยวกับเหตุผลของคณะกรรมการว่าเพราะเหตุใดจึงยังไม่ได้รับการเพิ่มแม้ว่าโดยส่วนตัวแล้วฉันคิดว่าการให้เหตุผลนั้นมีข้อบกพร่องพื้นฐานสำหรับเหตุผลที่ผู้คนชี้ไปแล้ว
คดีฟ้องร้องกองทุนโมนิก้า

-1

แก้ไข - เพื่อลบคำที่จำเป็นออกเพราะมันพิสูจน์แล้วว่ามีการโต้เถียง มันเป็นคำที่แน่นอนมากเกินไป

C ++ เป็นภาษาที่ใหญ่และซับซ้อนด้วยเหตุนี้คณะกรรมการมาตรฐานจึงรวมเฉพาะสิ่งที่เป็น จำเป็นอย่างยิ่ง มากที่สุดเท่าที่เป็นไปได้ถูกปล่อยให้เป็นห้องสมุดมาตรฐานที่ไม่ใช่ภาษา ... เช่น Boost
เพิ่ม :: คณิตศาสตร์ :: ค่าคงที่


29
แน่นอนว่าstd::hermiteและstd::cyl_bessel_iและstd::coshและstd::mersenne_twister_engineและstd::ranlux48และstd::cauchy_distributionและstd::assoc_laguerreและstd::betaทุกคนจำเป็นอย่างยิ่งที่เราใช้ทุกคนทุกวัน!
Amomum

2
ฉันค่อนข้างมั่นใจว่าคุณจะไม่ได้รับแม้แต่คณะกรรมการที่จะลงนามในความคิดที่ว่าทุกสิ่งที่พวกเขาลงคะแนนคือ "จำเป็นอย่างยิ่ง" มันไม่เกี่ยวกับความจำเป็น แต่เกี่ยวกับการเพิ่มคุณค่าให้กับภาษา - แทบจะไม่มีอะไรในไลบรารี่มาตรฐานที่จำเป็นสำหรับการเขียนโปรแกรมและสำหรับเรื่องนั้นภาษาหลักส่วนใหญ่ก็สามารถถูกโยนออกไปได้เช่นกัน (ถ้าคุณไม่สนใจการทำงานใน ทัวริง tarpit นั่นคือ) มูลค่าของการรวมค่าคงที่สำหรับ pi นั้นสามารถถกเถียงกันได้ แต่ความคิดที่ว่ามันไม่ได้อยู่ในนั้นเพราะมันไม่จำเป็นเพียงแค่ไม่ได้ถือน้ำ
Jeroen Mostert

อย่าบ่นมาที่ฉันฉันแค่อ้างถึงคณะกรรมการมาตรฐาน มีการพูดคุยกันอย่างเปิดกว้างเกี่ยวกับจำนวนการบูสต์ควรรวมอยู่ในมาตรฐาน C ++ 11 เหตุผลที่ทำให้ชุดย่อยขนาดเล็กดังกล่าวเกิดขึ้นเพราะผู้เขียนคอมไพเลอร์บ่นเกี่ยวกับการทดสอบที่เกี่ยวข้อง ดังนั้นหากมีบางสิ่งที่เป็นมาตรฐานก็มีเพราะมีคนคิดว่าจำเป็นต้องสร้างมาตรฐาน เพียงเพราะคุณไม่รู้ว่าทำไมไม่ได้หมายความว่าไม่มีเหตุผล
Tiger4Hire

1
@ Tiger4Hire ฉันแน่ใจว่ามีเหตุผลสำหรับทุกสิ่งฉันแค่ไม่เข้าใจว่าทำไมค่า pi จึงไม่เพิ่มเมื่อมีสิ่งที่ซับซ้อนมากขึ้น ค่าคงที่นั้นง่ายต่อการเขียนด้วยตัวแปรเท็มเพลตและไม่จำเป็นต้องทดสอบมากมายจากนักเขียนคอมไพเลอร์
Amomum

@ Amumum: ใช่การเพิ่ม pi ดูเหมือนจะเป็นค่าใช้จ่ายเล็กน้อยสำหรับชัยชนะครั้งใหญ่ ใจส่วนตัวฉันต้องการเห็น std :: network ก่อน std :: math :: constants
Tiger4Hire
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.