在 C++ 中除数时如何获得更准确的结果
How can I get a more accurate result when dividing numbers in C++
我正在尝试使用 C++ 估算 PI 作为一个有趣的数学项目。我 运行 遇到一个问题,我只能将其精确到小数点后 6 位。
我尝试使用浮点数而不是双精度数,但发现了相同的结果。
我的代码通过将 1/n^2
的所有结果相加来工作,其中 n=1 到定义的限制。然后它将此结果乘以 6 并取平方根。
Here is a link to an image written out in mathematical notation
这是我的 main
函数。 PREC
是预定义的限制。它将用这些分数的结果填充数组并得到总和。我的猜测是 sqrt
函数导致我无法获得比 6 位数更精确的问题。
int main(int argc, char *argv[]) {
nthsums = new float[PREC];
for (int i = 1; i < PREC + 1; i += 1) {
nthsums[i] = nth_fraction(i);
}
float array_sum = sum_array(nthsums);
array_sum *= 6.000000D;
float result = sqrt(array_sum);
std::string resultString = std::to_string(result);
cout << resultString << "\n";
}
为了这个目的,我还将包括我的求和函数,因为我怀疑它也可能有问题。
float sum_array(float *array) {
float returnSum = 0;
for (int itter = 0; itter < PREC + 1; itter += 1) {
if (array[itter] >= 0) {
returnSum += array[itter];
}
}
return returnSum;
}
我希望至少精确到 10 位数字。有没有办法在 C++ 中做到这一点?
因此,即使使用 long double
作为用于此的浮点类型,也需要一些微妙之处,因为添加两个数量级大不相同的 long double
会导致精度损失。请参阅 here 以了解 Java 中的讨论,但我相信它在 C++ 中的行为基本相同。
我使用的代码:
#include <iostream>
#include <cmath>
#include <numbers>
long double pSeriesApprox(unsigned long long t_terms)
{
long double pi_squared = 0.L;
for (unsigned long long i = t_terms; i >= 1; --i)
{
pi_squared += 6.L * (1.L / i) * (1.L / i);
}
return std::sqrtl(pi_squared);
}
int main(int, char[]) {
const long double pi = std::numbers::pi_v<long double>;
const unsigned long long num_terms = 10'000'000'000;
std::cout.precision(30);
std::cout << "Pi == " << pi << "\n\n";
std::cout << "Pi ~= " << pSeriesApprox(num_terms) << " after " << num_terms << " terms\n";
return 0;
}
输出:
Pi == 3.14159265358979311599796346854
Pi ~= 3.14159265349430016911469465413 after 10000000000 terms
9 位小数的准确度,这大约是我们对以此速率收敛的序列的期望值。
但如果我所做的只是颠倒 pSeriesApprox
中循环的顺序,添加完全相同的项,但从最大到最小而不是从最小到最大:
long double pSeriesApprox(unsigned long long t_terms)
{
long double pi_squared = 0.L;
for (unsigned long long i = 1; i <= t_terms; ++i)
{
pi_squared += 6.L * (1.L / i) * (1.L / i);
}
return std::sqrtl(pi_squared);
}
输出:
Pi == 3.14159265358979311599796346854
Pi ~= 3.14159264365071688729358356795 after 10000000000 terms
尽管我们使用了 100 亿个术语,但我们的准确性突然下降到了 7 位数。事实上,在大约 1 亿项之后,pi 的近似值稳定在这个特定值。因此,虽然使用足够大的数据类型来存储这些计算很重要,但在尝试执行此类求和时仍需要额外注意。
我正在尝试使用 C++ 估算 PI 作为一个有趣的数学项目。我 运行 遇到一个问题,我只能将其精确到小数点后 6 位。
我尝试使用浮点数而不是双精度数,但发现了相同的结果。
我的代码通过将 1/n^2
的所有结果相加来工作,其中 n=1 到定义的限制。然后它将此结果乘以 6 并取平方根。
Here is a link to an image written out in mathematical notation
这是我的 main
函数。 PREC
是预定义的限制。它将用这些分数的结果填充数组并得到总和。我的猜测是 sqrt
函数导致我无法获得比 6 位数更精确的问题。
int main(int argc, char *argv[]) {
nthsums = new float[PREC];
for (int i = 1; i < PREC + 1; i += 1) {
nthsums[i] = nth_fraction(i);
}
float array_sum = sum_array(nthsums);
array_sum *= 6.000000D;
float result = sqrt(array_sum);
std::string resultString = std::to_string(result);
cout << resultString << "\n";
}
为了这个目的,我还将包括我的求和函数,因为我怀疑它也可能有问题。
float sum_array(float *array) {
float returnSum = 0;
for (int itter = 0; itter < PREC + 1; itter += 1) {
if (array[itter] >= 0) {
returnSum += array[itter];
}
}
return returnSum;
}
我希望至少精确到 10 位数字。有没有办法在 C++ 中做到这一点?
因此,即使使用 long double
作为用于此的浮点类型,也需要一些微妙之处,因为添加两个数量级大不相同的 long double
会导致精度损失。请参阅 here 以了解 Java 中的讨论,但我相信它在 C++ 中的行为基本相同。
我使用的代码:
#include <iostream>
#include <cmath>
#include <numbers>
long double pSeriesApprox(unsigned long long t_terms)
{
long double pi_squared = 0.L;
for (unsigned long long i = t_terms; i >= 1; --i)
{
pi_squared += 6.L * (1.L / i) * (1.L / i);
}
return std::sqrtl(pi_squared);
}
int main(int, char[]) {
const long double pi = std::numbers::pi_v<long double>;
const unsigned long long num_terms = 10'000'000'000;
std::cout.precision(30);
std::cout << "Pi == " << pi << "\n\n";
std::cout << "Pi ~= " << pSeriesApprox(num_terms) << " after " << num_terms << " terms\n";
return 0;
}
输出:
Pi == 3.14159265358979311599796346854
Pi ~= 3.14159265349430016911469465413 after 10000000000 terms
9 位小数的准确度,这大约是我们对以此速率收敛的序列的期望值。
但如果我所做的只是颠倒 pSeriesApprox
中循环的顺序,添加完全相同的项,但从最大到最小而不是从最小到最大:
long double pSeriesApprox(unsigned long long t_terms)
{
long double pi_squared = 0.L;
for (unsigned long long i = 1; i <= t_terms; ++i)
{
pi_squared += 6.L * (1.L / i) * (1.L / i);
}
return std::sqrtl(pi_squared);
}
输出:
Pi == 3.14159265358979311599796346854
Pi ~= 3.14159264365071688729358356795 after 10000000000 terms
尽管我们使用了 100 亿个术语,但我们的准确性突然下降到了 7 位数。事实上,在大约 1 亿项之后,pi 的近似值稳定在这个特定值。因此,虽然使用足够大的数据类型来存储这些计算很重要,但在尝试执行此类求和时仍需要额外注意。