原题:
自然常数 e 可以用级数 1+1/1!+1/2!+⋯+1/n!+⋯ 来近似计算。本题要求对给定的非负整数 n,求该级数的前 n+1 项和。
输入格式:
输入第一行中给出非负整数 n(≤1000)。
输出格式:
在一行中输出部分和的值,保留小数点后八位。
输入样例:
10
输出样例:
2.71828180
一开始觉得这题还挺简单的,但是当看到n<=1000的时候心里就咯噔一下了,然而还是硬着头皮按函数的暴力思维写了下代码:
#include<bits/stdc++.h>
using namespace std;
long long sjc(int a)
{
long long mult=1;
for(int i=1;i<=a;i++)
{
mult*=i;
}
return mult;
}
int main()
{
int n;
cin>>n;
double sum=1;
for(int i=1;i<=n;i++)
{
sum+=(double)1/sjc(i);
}
printf("%.8lf",sum);
}
运行之后果不其然wa了,检测点3不过检,提示里就是最大n,很明显,爆精度了。
cout<<sjc(999)<<endl;的结果是inf。
然而这道题到底不是难题,高精度虽然也不算难敲,但是这么想的话一定涉及到了高精度浮点数,这样就显得无趣了,所以我去韩了个同学的成品:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
double mult = 1, sum=1;
for(int i=1; i<=n; i++)
{
mult *= i;
sum += 1.0/mult;
}
printf("%.8lf",sum);
return 0;
}
这其实就是最早的时候,没学函数时候我们的计算方式,结果很明显依然是会爆精度的,但是,它却可以轻而易举的过检测点3。
这到底是为什么呢?我打出了cout<<mult<<endl;编译器输出了0。
本质而言,这道题对于极大的n其实就是简单的无限大与无穷小的思想,1/inf无限小,对于1/999!的宏伟数据来说,哪怕很唬人的给了个保留8位小数,依然是无济于事的。
然而我后面再在错题上缝补了一下,结果居然跑起来了:
#include<bits/stdc++.h>
using namespace std;
double sjc(int a)
{
double mult = 1;
for (int i = 1; i <= a; i++)
{
mult *= i;
}
return mult;
}
int main()
{
int n;
cin >> n;
double sum = 1;
for (int i = 1; i <= n; i++)
{
sum += 1.0 / sjc(i);
}
printf("%.8lf", sum);
return 0;
}
这也让我产生了些好奇心,明明都是完全相同的数据,只因为数据类型的不同,就产生了这样的结果。
通过各种类型的切换,发现:
如果mult为double型,那么即使return时转换为long long,也会正常进行除运算
如果mult为longlong,那么即使return或者运算中强制转化为double,也会将sum直接变成inf而不进行任何有效运算
这与一些文章中说的c/c++中inf的运算并不一致,但具体原因我确实是不得而知了,希望有大佬可以解答一下。但是对我们来说的话,重要的经验就是在数据会变成inf的场景下避免使用long long