【洛谷 P2393】yyy loves Maths II

这一道题要排坑的是关于精度的问题。

题目很简单,要做的只有一下三点:

  1. 读数据
  2. 加数据
  3. 输数据

C++中,常用的输入方式有scanf()cin。首先我们以scanf()函数进行讲解,因为这两种输入方式的判断结束方式不一样。

使用sacnf()函数

scanf()函数其在输入结束(即读不到数据时)将返回常数EOF(即-1),而读到数据时会返回读到数据的个数。所以我们可以得出第一版代码:

1
2
3
4
5
6
7
8
9
10
11
12
#include<bits/stdc++.h>
using namespace std;
long double ans,tmp;
int main()
{
while((scanf("%Lf",&tmp))!=EOF)
{
ans+=tmp;
}
printf("%.5f",ans);
return 0;
}

Tips: scanf()函数输入long double类型时需使用%Lf占位符,但printf()函数无论输出float类型还是long double类型,均使用%f占位符。

代码已经写完,交上去却只有20分

我们可以写个程序来测试一下:

1
2
3
4
5
6
7
#include<bits/stdc++.h>
using namespace std;
long double tmp=1919810.1145142233;
int main(){
printf("%.20Lf",tmp);
return 0;
}

得到的输出是1919810.11451422329992055893,而不是我们预想的1919810.1145142233差的不是一丁半点。所以基本上可以判断为精度问题。

所以,我们需要先将它转换为整数,随后再转换回小数,以此确保精度不会出错。

1
2
3
4
5
6
7
8
9
10
11
12
#include<bits/stdc++.h>
using namespace std;
long double ans,tmp;
int main()
{
while((scanf("%Lf",&tmp))!=EOF)//判断是否结束
{
ans+=tmp*1000000;//化成整数,防止小数的精度问题
}
printf("%.5Lf",ans/1000000);//转换回小数
return 0;
}

由于每个数最多只有66位小数,所以这里只需要将读入的小数×1000000\times 1000000,就可以转换为整数,最后因为每个数都×1000000\times 1000000,所以我们只需要将最终的答案÷1000000\div 1000000,就可以恢复了。

使用cin

前面已经说明了具体的算法,故此处不再阐述。

cin读取完毕后,会直接返回00,而不是如同scanf()返回的EOF常数。所以,只需要修改判空部分:

1
2
3
4
5
6
7
8
9
10
11
12
#include<bits/stdc++.h>
using namespace std;
long double ans,tmp;
int main()
{
while((cin>>tmp)!=0)//判断是否结束,可以化简成while(cin>>tmp)
{
ans+=tmp*1000000;
}
cout<<fixed<<setprecision(5)<<ans/1000000;//使用cout保留小数
return 0;
}

Q&A

Q: 为什么用了你的代码没有输出啊?
A: 您在使用标准输入输出时,程序并不知道你接下来还要不要输入,故无法判断输入是否结束。所以你需要向程序发送一个“信号”,告诉程序输入已经终止。在Windows下,这个“信号”为在程序框中输入Ctrl+Z再按回车,而在类Unix系统下,这个“信号”为直接按Ctrl+D

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!

欢迎关注我的其它发布渠道