C ++ 11, 14, 17 hoặc 20 có đưa ra hằng số chuẩn cho pi không?


170

Có một vấn đề khá ngớ ngẩn với số pi trong C và C ++. Theo như tôi biết M_PIđược xác định trong math.hkhông được yêu cầu bởi bất kỳ tiêu chuẩn.

Các tiêu chuẩn C ++ mới đã giới thiệu rất nhiều phép toán phức tạp trong thư viện tiêu chuẩn - các hàm hyperbol, std::hermitestd::cyl_bessel_i, các bộ tạo số ngẫu nhiên khác nhau, v.v.

Có bất kỳ tiêu chuẩn 'mới' nào mang lại một hằng số cho pi không? Nếu không - tại sao? Làm thế nào để tất cả các toán học phức tạp này làm việc mà không có nó?

Tôi biết các câu hỏi tương tự về pi trong C ++ (chúng đã có vài năm và tiêu chuẩn cũ); Tôi muốn biết tình trạng hiện tại của vấn đề.

Tôi cũng rất quan tâm tại sao oh tại sao C ++ vẫn không có hằng số pi nhưng có nhiều phép toán phức tạp hơn.

CẬP NHẬT: Tôi biết rằng tôi có thể tự định nghĩa pi là 4 * atan (1) hoặc acos (1) hoặc double pi = 3.14. Chắc chắn rồi. Nhưng tại sao năm 2018 tôi vẫn phải làm điều đó? Làm thế nào để các hàm toán học tiêu chuẩn hoạt động mà không cần pi?

CẬP NHẬT2: Theo báo cáo chuyến đi này cho cuộc họp của Ủy ban C ++ vào tháng 7 năm 2019 tại Cologne, đề xuất P0631 (hằng số toán học) đã được chấp nhận vào C ++ 20. Vì vậy, có vẻ như cuối cùng chúng ta sẽ có số pi trong thư viện tiêu chuẩn!


Bạn lưu ý sự tồn tại của các câu hỏi cũ như hằng số pi độc lập nền tảng tốt nhất? . Nếu bạn lo lắng họ đã hết hạn, bạn luôn có thể đặt tiền thưởng cho một trong số họ yêu cầu câu trả lời dựa trên C ++ 17, v.v ... Sau đó, tất cả các câu trả lời sẽ ở một nơi. Tại sao vẫn là một câu hỏi hay nhưng có lẽ điều này nên tập trung vào lý do tại sao và yêu cầu cập nhật nên là một phần thưởng cho các câu hỏi hiện có.
Shafik Yaghmour

Tôi nghĩ rằng có thể đáng để thêm câu trả lời mới kể từ khi C ++ 20 thêm hằng số pi theo như tôi biết
Guillaume Racicot

@GuillaumeRacicot tôi đã cập nhật câu hỏi. Không chắc chắn nếu chúng ta nên giải quyết C ++ 20 vì nó chưa chính thức ra mắt.
Amomum

@GuillaumeRacicot: Sẽ hơi muộn khi thêm một người
Davis Herring

Câu trả lời:


101

Lên đến và bao gồm C ++ 17 pi không phải là một hằng số được đưa vào ngôn ngữ, và đó là một nỗi đau ở cổ.

Tôi may mắn khi tôi sử dụng boost và họ xác định pi với số lượng thập phân đủ lớn thậm chí là 128 bit long double.

Nếu bạn không sử dụng Boost thì hãy tự mã hóa nó. Xác định nó bằng một hàm lượng giác rất hấp dẫn nhưng nếu bạn làm điều đó thì bạn không thể làm được constexpr. Độ chính xác của các hàm lượng giác cũng không được bảo đảm bằng bất kỳ tôi biết tiêu chuẩn của ( cf . std::sqrt), Do đó thực sự bạn đang ở trên mặt đất nguy hiểm thực sự dựa vào một chức năng như vậy.

Có một cách để nhận constexprgiá trị cho pi bằng cách sử dụng siêu lập trình: xem http://timmurphy.org/2013/06/27/template-metaprogramming-in-c/


Từ C ++ 20 một số tin tốt. Có một defininition cho pi . C ++ 20 thêm một số hằng số toán học trong <numbers>. Ví dụ std::numbers::pilà một doubleloại.

Tham khảo: https://en.cppreference.com/w/cpp/numeric/constants


9
@Lundin: Nhưng đó không phải là constexprđiều đáng tiếc, đó là lý do tại sao tôi nói "Xác định nó bằng chức năng trig là một nỗi đau"
Bathsheba

9
Tại sao nó quan trọng? Pi là một hằng số, không có thể thay đổi hoặc bất cứ điều gì cụ thể thực hiện. Chỉ cần viết ra giá trị (trong một đối tượng const nếu bạn muốn) đến số lượng địa điểm quan trọng cho double(hoặc một số vô lý nếu bạn quan tâm đến giả thuyết thực sự dài gấp đôi dài).
R .. GitHub DỪNG GIÚP ICE

10
@ R..Những vấn đề tôi gặp phải đó là những gì có vẻ vô lý bây giờ có thể trở nên hoàn toàn lành mạnh trong thời gian 20 năm hoặc lâu hơn. (Tâm trí 640k của Bill Gates). Tôi tin tưởng Boost để theo kịp sự phát triển kiến ​​trúc.
Bathsheba


10
@KonradRudolph: Một pi có độ chính xác cao có vấn đề nếu thực hiện giảm phạm vi. Ví dụ: hằng số Pi bên trong của x86 / x87 (mantissa đầy đủ 64 bit) dẫn đến lỗi trong trường hợp xấu nhất đối với các đầu vào nhỏ theo fsinhướng dẫn "khoảng 1,37 triệu đơn vị ở vị trí cuối cùng, để lại ít hơn bốn bit" và đó là thậm chí tệ hơn đối với các đầu vào lớn trong đó phạm vi giảm phạm vi bao quanh nhiều lần. Điều này có phần tiếp tuyến với các hằng số được sử dụng long doubletrong C ++, nhưng dù sao cũng gọn gàng.
Peter Cordes

31

Như những người khác nói không có std::pinhưng nếu bạn muốn PIgiá trị chính xác, bạn có thể sử dụng:

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

Điều này giả định rằng C ++ thực hiện của bạn tạo ra một giá trị chính xác toàn diện của PI từ acos(-1.0), mà là phổ biến nhưng không được bảo đảm .

Không phải vậy constexpr, nhưng trong thực tế tối ưu hóa các trình biên dịch như gcc và clang đánh giá nó tại thời gian biên dịch. Tuyên bố rằng điều constquan trọng là tối ưu hóa để làm một công việc tốt, mặc dù.


2
Điều này là nguy hiểm vì acos()hàm có độ dốc vô hạn tại x = -1. Do đó, phương pháp này dựa vào việc acos()triển khai để cơ bản nắm bắt rõ ràng trường hợp của một -1đối số chính xác và trả về hằng số chính xác trực tiếp. Sử dụng tốt hơn một cái gì đó giống như 4*atan(1)toán học mạnh mẽ hơn nhiều (độ dốc ứng xử tốt x = 1và nhân với 4 luôn chính xác với toán học dấu phẩy động).
cmaster - phục hồi monica

1
Chúng tôi không được phép sử dụng std::acostrong một biểu thức không đổi. clang báo cáo đây là lỗi. Vui lòng lưu ý rằng đây là một phần mở rộng không phù hợp và cuối cùng nên được sửa trong gcc. Vui lòng tham khảo câu trả lời này để biết thêm chi tiết.
badola

31

Lên đến C ++ 20, không, không có tiêu chuẩn nào đưa ra hằng số đại diện cho số pi (π). Bạn có thể tính gần đúng số trong mã của mình:

constexpr double pi = 3.14159265358979323846;

Các ngôn ngữ khác như C # hằng số được khai báo trong thư viện của chúng.

Cập nhật: Bắt đầu với C ++ 20, thực sự có một pihằng số được khai báo bên trong <numbers>tiêu đề. Nó được truy cập thông qua : std::numbers::pi.


6
Bạn có thể muốn thêm inlinecho C ++ 17 +.
Ded repeatator

8
Ta. Có một upvote, nhưng lưu ý rằng định nghĩa của bạn vẫn dễ bị thiếu chính xác với các nền tảng với các định nghĩa khác nhau double. C # có dễ dàng vì doubleloại đã được sửa. Nếu tôi ở trong ủy ban tiêu chuẩn C ++, tôi sẽ đề xuất một cái gì đó nhưstd::constants<double>::pi
Bathsheba

11
@Ded repeatator không phải là constexpr hoàn toàn nội tuyến ...?
whn

4
@R. Đủ công bằng, mặc dù bạn nên khẳng định tĩnh std::numeric_limits<double>::is_iec559;trong trường hợp đó. Mà, tôi thú nhận, là những gì tôi có trong "tiêu đề chính" của mình. Lưu ý rằng chính thức bạn cần kiểm tra riêng tất cả các loại dấu phẩy động. Chỉ vì một là IEEE754 không có nghĩa là tất cả chúng đều như vậy.
Bathsheba

2
@DanielSchepler Vậy, "số" đó là gì? Tôi không biết có hai số với 16 cơ sở.
BЈовић

29

M_PIđược định nghĩa bởi "một tiêu chuẩn", nếu không phải là một tiêu chuẩn ngôn ngữ : POSIX với phần mở rộng Giao diện hệ thống X / Open (thường được hỗ trợ và yêu cầu cho việc xây dựng thương hiệu UNIX chính thức).

Nó (vẫn) không chắc chắn những gì sẽ có trong C ++ 20, nhưng vì bạn đã hỏi: nó có thể sẽ có các hằng số như vậy . Bài viết đã được hợp nhất trong vòng cuối cùng của các tính năng C ++ 20 (cho Dự thảo của Ủy ban vào tháng 8 năm 2019).

Cụ thể, sẽ có cả std::numbers::pi(loại double) và mẫu biến mà bạn có thể sử dụng nếu bạn muốn loại dấu phẩy động khác nhau, vd std::numbers::pi_v<float>. Danh sách đầy đủ các hằng số có thể được nhìn thấy trong [Numbers.syn] .


Vui lòng chỉnh lại chỉnh sửa của tôi khi bạn thấy phù hợp - Tôi chỉ muốn thêm chính tả thực sự của các hằng số mới ở đây cho hậu thế.
Barry

12

Đây rõ ràng không phải là một ý tưởng tốt bởi vì không có loại rõ ràng nào xác định pi được áp dụng phổ biến trên các miền.

Tất nhiên, Pi là một số vô tỷ nên nó không thể được biểu diễn chính xác bởi bất kỳ loại C ++ nào. Do đó, bạn có thể lập luận rằng cách tiếp cận tự nhiên là xác định nó theo kiểu dấu phẩy động lớn nhất hiện có. Tuy nhiên, kích thước của loại dấu phẩy động tiêu chuẩn lớn nhất long doublekhông được xác định bởi tiêu chuẩn C ++ nên giá trị của hằng số sẽ khác nhau giữa các hệ thống. Tồi tệ hơn, đối với bất kỳ chương trình nào trong đó loại làm việc không phải là loại lớn nhất, định nghĩa về pi sẽ không phù hợp vì nó sẽ áp đặt chi phí hiệu năng cho mỗi lần sử dụng pi.

Bất kỳ lập trình viên nào cũng tìm thấy giá trị của pi và xác định hằng số riêng của họ phù hợp để sử dụng, vì vậy nó không cung cấp bất kỳ lợi thế lớn nào để đưa nó vào các tiêu đề toán học.


10
C ++ 14 đã cho chúng tôi các mẫu biến. Đó không phải là những gì họ đang làm?
Amomum

21
nói như một thành viên ủy ban thực thụ, nói về tất cả những cách nó không thể được thực hiện mặc dù con đường rõ ràng phía trước được trình bày. Xem ví dụ mẫu biến có liên quan đáng kinh ngạc . Tôi không nghĩ câu trả lời của bạn là xấu, nhưng tôi chắc chắn rằng nó sẽ không giúp Bjarne Stroustrup chán nản vĩnh viễn về sự hối tiếc khi trao quyền kiểm soát tương lai của C ++ cho một ủy ban rất thiếu quyết đoán.
whn

4
@snb Tôi đồng ý rằng việc tạo pimột hằng số đa hình là con đường rõ ràng phía trước - trong một ngôn ngữ với suy luận kiểu Hindley-Milner. Trong Haskell, chúng ta luôn có pi :: Floating a => a, do đó, nó pisẽ tự động có giá trị 3.1415927trong một Floatbối cảnh, 3.141592653589793trong một Doublebối cảnh và πtrong một bối cảnh tính toán mang tính biểu tượng. Nhưng mọi người có thực sự muốn phải khởi tạo rõ ràng tham số mẫu không? Có vẻ hơi khó xử, đặc biệt là nếu long doubleviệc triển khai cố định sẽ cho kết quả giống hệt nhau trong hầu hết các ứng dụng.
leftaroundabout

2
@leftaroundabout Tôi tin rằng văn bản auto a = pi<float>;là hoàn toàn tốt, chắc chắn sẽ dễ đọc hơn sau đó4*atan(1)
Amomum

1
Ugh, tôi đã đánh giá thấp điều này bằng cách bấm sai và bây giờ tôi không thể hoàn tác nó. Lấy làm tiếc. Điều này xứng đáng được +1 cho một lời giải thích khá hay về lý do của ủy ban về lý do tại sao nó không được thêm vào, ngay cả khi cá nhân tôi nghĩ rằng lý do này về cơ bản là thiếu sót vì những lý do mà mọi người đã chỉ ra.
Vụ kiện của Quỹ Monica

-1

Đã chỉnh sửa - Để xóa thuật ngữ cần thiết, vì nó đã gây tranh cãi. Đó là quá nhiều của một thuật ngữ tuyệt đối.

C ++ là một ngôn ngữ lớn và phức tạp, vì lý do đó, Ủy ban Tiêu chuẩn chỉ bao gồm những thứ được yêu cầu mạnh mẽ . Càng nhiều càng tốt để các thư viện tiêu chuẩn phi ngôn ngữ ... như Boost.
boost :: math :: hằng số


29
Chắc chắn, std::hermitestd::cyl_bessel_istd::coshstd::mersenne_twister_enginestd::ranlux48std::cauchy_distributionstd::assoc_laguerrestd::betatất cả là hoàn toàn cần thiết, tất cả chúng ta sử dụng chúng mỗi ngày!
Amomum

2
Tôi khá chắc chắn rằng bạn thậm chí không thể tự mình đăng ký với ý tưởng rằng mọi thứ họ bỏ phiếu là "hoàn toàn cần thiết". Đó không phải là sự cần thiết mà là về việc tăng thêm giá trị cho ngôn ngữ - hầu như không có gì trong thư viện chuẩn là cần thiết để viết chương trình, và đối với vấn đề đó, hầu hết ngôn ngữ cốt lõi cũng có thể bị loại bỏ (nếu bạn không bận tâm làm việc trong Turing tarpit, đó là). Giá trị của việc đưa vào hằng số cho pi có thể được tranh luận, nhưng ý tưởng rằng nó không có trong đó bởi vì nó không cần thiết không giữ nước.
Jeroen Mostert

Đừng phàn nàn với tôi, tôi chỉ trích dẫn ủy ban tiêu chuẩn. Đã có những cuộc thảo luận mở dài về việc tăng bao nhiêu trong tiêu chuẩn C ++ 11. Lý do tại sao một tập hợp nhỏ như vậy được thực hiện là bởi vì các nhà văn trình biên dịch phàn nàn về bao nhiêu thử nghiệm có liên quan. Do đó, nếu có thứ gì đó nằm trong tiêu chuẩn, thì đó là vì ai đó nghĩ cần phải tiêu chuẩn hóa nó. Chỉ vì bạn không biết tại sao, không có nghĩa là không có lý do.
Tiger4Hire

1
@ Tiger4Hire Tôi chắc chắn có lý do cho tất cả mọi thứ, tôi chỉ không thể hiểu tại sao hằng số pi không được thêm vào khi có nhiều thứ phức tạp hơn. Constant rất dễ viết với các mẫu biến và sẽ không yêu cầu nhiều thử nghiệm từ các trình soạn thảo trình biên dịch.
Amomum

@Amomum: Vâng, thêm pi có vẻ như một chi phí nhỏ cho một chiến thắng lớn. Tâm trí, cá nhân tôi muốn xem std :: mạng trước std :: math :: hằng số.
Tiger4Hire
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.