【问题标题】:Calculate PI up to 42 decimal places计算 PI 至小数点后 42 位
【发布时间】:2020-02-10 02:05:02
【问题描述】:

我正在尝试编写一个程序,使用该系列来计算 PI 的值。用户将输入希望程序计算系列的距离,然后程序应输出其计算的 PI 值。我相信我已经成功地为此编写了代码,但是它不适用于大数字,并且只给了我几个小数位。当我尝试使用cout << fixed << setprecision(42); 时,它只是给了我“nan”作为 PI 的值。

int main() {

    long long seqNum; // sequence number users will input
    long double val; // the series output

    cout << "Welcome to the compute PI program." << endl; // welcome message
    cout << "Please inter the sequence number in the form of an integer." << endl;
    cin >> seqNum; // user input

    while ( seqNum < 0) // validation, number must be positive
    {
        cout << "Please enter a positive number." << endl;
        cin >> seqNum;
    } // end while

    if (seqNum > 0)
    {
        for ( long int i = 0; i < seqNum; i++ )
        {
            val = val + 4*(pow(-1.00,i)/(1 + 2*i)); // Gregory-Leibniz sum calculation
        }// end for


        cout << val;
    } // end if

    return 0;
}

任何帮助将不胜感激。谢谢

【问题讨论】:

  • 一个典型的long double 只能表示大约 35 位有效数字的值。例如,IEEE754 四精度浮点——这是long double 非常常用的格式——只保证最多 34 位有效数字。这意味着对于 pi 的小数点后 42 位的要求在大多数实际实现中是无法实现的。此外,在您的代码中,val 未初始化,第一次使用会访问其值 - 这会产生未定义的行为。
  • 我明白了,所以即使我使用 setprecision(42),第 35 位之后的任何内容都会是 goop?另外,我不确定我的 val 未初始化是什么意思。我以为我在顶部这样做是通过声明它是一个长的双倍
  • 定义long double val 表示val 已定义但未初始化。访问未初始化变量的值会产生未定义的行为。 val 在嵌套循环中的第一次用法是 val = val + 4*(pow(-1.00,i)/(1 + 2*i));,这意味着 val 的新值是使用其先前值计算的 - 所以第一次发生时,val 未初始化,并且行为未定义。并且(这是您所做的一个常见但有缺陷的假设)long double val 不需要将 val 初始化为零或任何其他特定值。
  • @Peter long double 通常不是 IEEE754 四倍精度,而是 x86 上的 80 位扩展精度
  • pow(-1.00,i) 是一种获取交替符号的效率极低的方法。只需使用类似i % 2 == 0 ? 1 : -1

标签: c++ pi


【解决方案1】:

您的问题涉及与double 值相关的基本原则:double 或任何浮点类型只能包含有效数字的固定上限。普通的、普通的doubles 没有无限的精度数字。有一个严格的上限。确切的限制是实现定义的,但在现代 C++ 实现中,典型的限制只有 16 或 17 位精度,甚至没有接近您想要的 42 位精度。

#include <limits>
#include <iostream>

int main()
{
    std::cout << std::numeric_limits<double>::max_digits10 << std::endl;
    return 0;
}

这为您的平台/C++ 编译器提供了最大精度数。这显示了 Linux 上 g++ 9.2 的最大 17 位精度(max_digits10 是 C++11 或更高版本,将 digits10 与旧 C++ 编译器一起使用以显示密切相关的指标)。

您想要的 42 位精度可能远远超出您的 doubles 可以处理的精度。有各种特殊用途的数学库可以执行更高精度的计算,如果您愿意,可以研究这些。

【讨论】:

  • 是的,没错。
  • 啊,好的。难怪我的电脑不喜欢这个代码。谢谢!
【解决方案2】:

您没有对val 进行初始化或分配任何值,但是当您进行第一次迭代时正在阅读它

val = val + 4*(pow(-1.00,i)/(1 + 2*i));

这会导致您的程序拥有undefined behavior。初始化val,可能为零:

long double val = 0; // the series output

除此之外,正如@SamVarshavchik 的回答中提到的那样,内置浮点类型可以达到的精度有一个硬性限制,42 位的重要性几乎肯定不在此范围内。同样,您使用的整数类型的大小可能最多为2^64,大约为10^19

即使这些限制不是问题,该系列也需要对大约 10^42 项求和,以使 PI 的精度达到 42 位。将地球当前的所有计算能力结合起来,计算到这个精度需要的时间比宇宙存在的时间还要长。

【讨论】:

  • 现在我明白彼得所说的我的 val 未初始化是什么意思。非常感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-18
相关资源
最近更新 更多