【问题标题】:Does C++11, 14, 17 or 20 introduce a standard constant for pi?C++11、14、17 或 20 是否为 pi 引入了标准常量?
【发布时间】:2018-09-21 12:37:12
【问题描述】:

C 和 C++ 中的数字 pi 存在一个相当愚蠢的问题。据我所知,math.h 中定义的M_PI 不是任何标准所要求的。

新的 C++ 标准在标准库中引入了许多复杂的数学 - 双曲函数、std::hermitestd::cyl_bessel_i、不同的随机数生成器等等。

是否有任何“新”标准为 pi 引入了常数?如果不是 - 为什么?没有它,所有这些复杂的数学如何运作?

我知道关于 C++ 中的 pi 的类似问题(它们已经有好几年了,而且标准老了);我想知道问题的当前状态。

我也对为什么哦为什么C++ 仍然没有 pi 常数但有很多更复杂的数学很感兴趣。

我知道我可以将 pi 自己定义为 4*atan(1)acos(-1)double pi = 3.14;。当然。但是我为什么还要这样做呢?没有 pi,标准数学函数如何工作?

【问题讨论】:

  • 您注意到存在诸如Best platform independent pi constant? 之类的旧问题。如果您担心它们已过时,您总是可以悬赏其中一个要求基于 C++17 等的答案......然后所有答案都将在一个地方。为什么仍然是一个很好的问题,但也许这应该关注为什么并且要求更新应该是对现有问题的奖励。
  • 我认为可能值得添加新答案,因为据我所知,C++20 添加了一个 pi 常量
  • @GuillaumeRacicot 我更新了这个问题。不确定我们是否应该解决 C++20,因为它还没有正式发布。
  • @GuillaumeRacicot:添加一个有点晚了……
  • 我想指出 pi 等于 arccos(-1),而不是 arccos(1),但更改的字符太少,我无法编辑问题。

标签: c++ c++11 c++14 language-lawyer c++17


【解决方案1】:

直到并包括 C++17 pi 不是引入到语言中的常量,这是一个令人头疼的问题。

我很幸运,因为我使用了boost,他们为 pi 定义了足够多的小数位数,即使是 128 位 long double

如果您不使用 Boost,请自行对其进行硬编码。用三角函数定义它很诱人,但如果你这样做,你就不能让它成为constexpr。我所知道的任何标准也不能保证三角函数的准确性(cfstd::sqrt),所以你确实处于危险的境地,确实依赖这样的函数。

有一种方法可以使用元编程为 pi 获取 constexpr 值:请参阅 http://timmurphy.org/2013/06/27/template-metaprogramming-in-c/


来自 C++20 的一些好消息。 pi定义。 C++20 在<numbers> 中添加了一些数学常数。例如std::numbers::pidouble 类型。

参考:https://en.cppreference.com/w/cpp/numeric/constants

【讨论】:

  • @Lundin:但不幸的是,这不可能是constexpr,这就是为什么我说“用三角函数定义它很痛苦”
  • 为什么重要? Pi 是一个常数,不会更改或任何特定于实现的内容。只需将值(如果您愿意,可以在 const 对象中)写出对double 有意义的位置数(或者如果您关心假设的非常长的长双精度数,则可以是一些荒谬的数字)。
  • @R..我遇到的问题是,现在看起来很荒谬的事情可能会在 20 年左右的时间里变得完全理智。 (比尔盖茨的 640k 浮现在脑海中)。我相信 Boost 能够跟上架构发展的步伐。
  • @KonradRudolph:如果实施范围缩小,高精度 pi 很重要。例如,x86/x87 的内部 Pi 常数(完整的 64 位尾数)会导致对 "about 1.37 quintillion units in the last place, leaving fewer than four bits correct"fsin 指令的小输入出现最坏情况错误,而对于范围缩小的大输入则更糟多次。这与 C++ 中用于 long double 的常量有些相切,但无论如何都很整洁。
【解决方案2】:

直到 C++20,不,没有一个标准引入了表示number pi (π) 的常量。您可以近似代码中的数字:

constexpr double pi = 3.14159265358979323846;

其他语言如 C# have 在其库中声明的常量。

更新: 从 C++20 开始,在 <numbers> 标头中确实声明了一个 pi 常量。访问方式为:std::numbers::pi

【讨论】:

  • 您可能想为 C++17+ 添加inline
  • Ta。投赞成票,但请注意,您的定义仍然容易受到具有不同 double 定义的平台的影响。因为double 类型是固定的,所以C# 很容易。如果我是 C++ 标准委员会的成员,我会提出类似 std::constants<double>::pi 的建议
  • @Deduplicator 也不是 constexpr 隐式内联...?
  • @R。很公平,尽管在这种情况下您应该在std::numeric_limits<double>::is_iec559; 上进行静态断言。我承认,这就是我的“主标题”中的内容。请注意,正式地您需要分别检查所有浮点类型。仅仅因为一个是 IEEE754 并不意味着它们都是。
  • @DanielSchepler 那么,那个“数字”应该是什么?我不知道有 16 个基数的双数。
【解决方案3】:

正如其他人所说,没有std::pi,但如果您想要精确的PI 值,您可以使用:

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

这假定您的 C++ 实现从acos(-1.0)which is common but not guaranteed 生成正确舍入的 PI 值。

不是constexpr,而是在实践中优化编译器如 gcc 和 clang 在编译时评估它。不过,将其声明为 const 对于优化器做好工作很重要。

【讨论】:

  • 这很危险,因为acos() 函数在x = -1 处具有无限斜率。因此,此方法依赖于 acos() 实现来基本上显式地捕获精确的 -1 参数的情况,并直接返回正确的常量。最好使用像 4*atan(1) 这样的东西,它在数学上更加稳健(x = 1 处的良好斜率和浮点数学乘以 4 总是精确的)。
  • 我们不允许在常量表达式中使用std::acos。 clang 将此报告为错误。请注意,这是一个不符合标准的扩展,最终应该在 gcc 中修复。请参考answer了解更多详情。
【解决方案4】:

M_PI 由“标准”定义,如果不是语言 标准:POSIX 带有 X/Open 系统接口扩展(这是非常普遍的支持和官方 UNIX 品牌所必需的) )。

与此同时,C++20 确实有 such constants(在最后一轮 C++20 特性中合并)。具体来说,有std::numbers::pidouble 类型)和一个变量模板,如果您想要不同的浮点类型,可以使用它们,例如std::numbers::pi_v<float>.

【讨论】:

  • 如果您认为合适,请随意改写我的编辑 - 我只是想在此处添加新常量的实际拼写以供后代使用。
【解决方案5】:

这显然不是一个好主意,因为没有明显的类型可以用来定义 pi 可以普遍适用于各个领域。

当然,Pi 是一个无理数,因此它不能被任何 C++ 类型正确表示。因此,您可能会争辩说,自然的方法是将其定义为可用的最大浮点类型。但是,最大标准浮点类型 long double 的大小未由 C++ 标准定义,因此常量的值会因系统而异。更糟糕的是,对于工作类型不是这个最大类型的任何程序,pi 的定义是不合适的,因为它会在每次使用 pi 时产生性能成本。

对于任何程序员来说,找到 pi 的值并定义自己的适合使用的常量也是微不足道的,因此将它包含在数学头文件中并没有太大的优势。

【讨论】:

  • C++14 为我们提供了变量模板。这不是他们的目的吗?
  • 像真正的委员会成员一样说话,谈论尽管提出了明确的前进道路但无法完成的所有方式。 See the suprisingly relevant example Variable Templates。我不认为你的回答是坏的,但我相信这不会帮助 Bjarne Stroustrup 对将 C++ 未来的控制权交给一个非常优柔寡断的委员会感到遗憾而永远沮丧。
  • @snb 我同意将pi 设为多态常量是一条清晰的前进道路——在具有 Hindley-Milner 类型推断的语言中。在 Haskell 中,我们一直有 pi :: Floating a => a,所以 pi 会在 Float 上下文中自动具有值 3.14159273.141592653589793Double 上下文中自动具有值,π 在符号-计算上下文。但是人们真的愿意显式地实例化模板参数吗?似乎有点尴尬,特别是如果一个固定的long double 实现会在大多数应用程序中给出相同的结果。
  • @leftaroundabout 我相信写auto a = pi<float>; 完全没问题,当然比臭名昭著的4*atan(1) 更具可读性
  • 呃,我被误击否决了,现在我无法撤消它。对不起。这值得 +1 以很好地解释委员会为什么没有添加它的原因,即使我个人认为由于人们已经指出的原因,该推理存在根本性的缺陷。
【解决方案6】:

已编辑 - 删除必要的术语,因为它被证明是有争议的。这太绝对了。

C++ 是一种庞大而复杂的语言,因此标准委员会只包含强烈要求的内容。尽可能多地留给非语言标准库...例如 Boost。
boost::math::constants

【讨论】:

  • 当然,std::hermitestd::cyl_bessel_istd::coshstd::mersenne_twister_enginestd::ranlux48std::cauchy_distributionstd::assoc_laguerrestd::beta 都是绝对必要的,我们都使用它们每天!
  • 我敢肯定,你甚至无法让委员会本身同意他们投票的所有内容都是“绝对必要的”这一想法。这与必要性无关,而是为语言增加价值——标准库中几乎没有是编写程序所必需的,为此,大多数核心语言也可以被抛弃(如果你不介意在 Turing tarpit 中工作,也就是说)。为 pi 包含一个常数的价值是有争议的,但认为它不存在是因为它只是没有必要的想法并不成立。
  • 别抱怨我,我只是引用标准委员会的话。关于应该在 C++11 标准中包含多少提升的公开讨论已经进行了很长时间。之所以会出现这么小的子集,是因为编译器编写者抱怨涉及到多少测试。因此,如果某些东西在标准中,它就在那里,因为有人认为有必要对其进行标准化。不知道为什么,不代表没有理由。
  • @Tiger4Hire 我确信一切都是有原因的,我只是不明白为什么在很多更复杂的事情时没有添加 pi 的常量。使用变量模板可以轻松编写常量,并且不需要编译器编写者进行大量测试。
  • @Amomum :是的,添加 pi 似乎只是为了获得巨大的胜利而产生的小开销。请注意,我个人更愿意在 std::math::constants 之前看到 std::network。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-03
相关资源
最近更新 更多