主页
搜索
最近更新
数据统计
申请密钥
系统公告
1
/
1
请查看完所有公告
题解:B2022 输出保留 12 位小数的浮点数
最后更新于 2025-07-31 10:39:01
作者
wangbinfeng
分类
题解
题解
B2022
复制 Markdown
查看原文
删除文章
更新内容
[   ](https://www.luogu.com.cn/problem/B2022) [-purple)](https://www.luogu.com.cn/user/387009) --- [$ \color{#00B8D4} \rule{2pt}{44pt} \color{#E5F8FB} \rule[24pt]{365pt}{20pt} \color{#e8e8e8}\rule{0.5pt}{44pt} \color{#f5f5f5}\rule{0.5pt}{44pt} \color{#fafafa}\rule{0.5pt}{44pt} \kern{-365pt}\kern{-1.5pt} \color{#bfbfbf}\rule[0pt]{365pt}{0pt}\kern{-365pt} \color{#d6d6d6}\rule[-0.5pt]{365pt}{0pt}\kern{-365pt} \color{#ececec}\rule[-1pt]{365pt}{0pt}\kern{-365pt} \color{#f8f8f8}\rule[-1.5pt]{365pt}{0pt}\kern{-365pt} \color{black} \raisebox{24pt}{ \raisebox{6pt}{ \kern{-1pt} \color{#00B8D4}\large{\kern{2pt}\bf{i}\kern{5.5pt}} \raisebox{1.5pt}{ \color{#404040}\footnotesize \kern{-4pt}\sf\bf{作者提示} }}}\kern{-200pt}$. $\kern{1pt}\raisebox{10pt}{\footnotesize\sf{\color{black}{本题删除了争议数据,但是处于严谨的考虑还是建议学会本题解提供的做法。具体可点击本提示查看。}}}$](https://lglg.top/r/6956713) > 本题的本意想考察各种语言输出时控制小数位数,但是这种做法本来就是错误的,且目前已有的题解也全部是严重错误的,因此我重构了题解。不过在这篇题解中,我想讲一下题目的本意,再讲正确的做法。首先你应当会控制输出时的小数位数,具体可以前往我写的 [B2021](https://www.luogu.com.cn/problem/B2021) 题的[题解](https://www.luogu.com.cn/article/rec0f3xq)查看题目本意要求的做法(请注意,你只应当查看我写的题解,而暂时不要阅读其它题解,因为全部是错误的,具体理由可以看[这里](https://www.luogu.com.cn/discuss/919282)。 我们考虑到将 B2021 的代码直接带到这里,并且修改以下内容即可 AC 本题 ~~(目前题目数据是错误的,如果你 WA 且仅 WA 掉了 #4,那么恭喜你做对了本题)~~: 1. 将 `float` 修改为 `long double`; 2. 将 $eps$ 从 $1\times10^{-9}$ 改为 $1\times10^{-15}$; 3. 将输出时的格式符改掉,具体地,对于 C 语言为 `printf("%.12Lf", f)`, 对于 C++ 语言为 `cout << fixed << setprecision(12) << f`, 对于 Python 语言为 `print(f"{f:.12f}")`, 对于 java 语言为 `String formattedF = String.format("%.12f", f)`, 对于 Pascal 语言为 `Str(f:0:12, formattedString)`。 咋一看这么做好像挺对的,毕竟考虑过浮点数的误差了。但是考虑这个数据:`0.499999999...`(暂时保留一位小数),数学上的答案显然应该是 `0.4`,但是由于误差和 $eps$ 的原因很可能被代码误认为是 `0.5`。那么有没有办法处理这种数据呢?答案是有的。因为输入的数据是十进制,那么保证了十进制数是不存在误差的。那么误差的来源就是进制转换,即我们不能在代码中有进制转换出现。 那么,`float/double/long double` 等自带的浮点数显然就不能用了,我们只能自己写一个浮点数处理的程序。存储输入的浮点数我们可以采用一个数组来存储每一位的数值(我的代码中使用了字符串,实际上它们的作用是相同的),然后对这个数组进行操作。 全部的流程如下,读者可以把本题当成一个普通的模拟题来完成: 1. 如果输入的是负数,可以把负号提取出来,然后就不存在负数了。 2. 如果输入的是整数,可以考虑直接输出,也可以考虑用第 3. 步的方法。 3. 若输入的小数位数不足 $12$ 位,则补足 $12$ 位(当然也可以补充更多的位数,比如为了方便判断四舍五入而补足 $13$ 位)。 4. 判断小数部分第 $13$ 位的数值,如果大于等于 $5$ 则向上进一位,否则不对上一位产生影响。 5. 若第 $12$ 位进位变成了 $10$,但由于十进制中没有 $10$ 这个数字(数字只单独一位的数值),将这一位补零并向上一位进位。 6. 重复第 5. 步直到没有任何一位存在 $10$。 7. 注意小数部分最高位进位需要进到整数部分,而整数部分最高位如果进位会导致结果答案变长。注意特判即可。 这道题可以练习大家的代码功底,作者本人暂且只提供 C++ 语言的代码,其他语言请大家自行练习。 ```cpp #include<bits/stdc++.h> using namespace std; string s; int i; bool flag_zero,flag_fu,flag_jw; signed main(){ cin>>s; for(i=0;s[i];i++) if(s[i]=='.')break; if(i==s.length()){ cout<<s<<'.'; for(int i=1;i<=12;i++)cout<<'0'; return 0; } if(s[0]=='0')flag_zero=true; if(s[0]=='-')s[0]='0',flag_fu=true; for(int j=1;j<=14;j++)s+='0'; if(s[i+13]>='5')s[i+12]++; for(int j=i+12;j>i;j--)if(s[j]>'9')s[j]='0',s[j-1]++; if(s[i]>'.')s[i]='.',s[i-1]++; for(;i>=0;i--)if(s[i]>'9'){ if(i==0)flag_jw=true; else s[i-1]++; s[i]='0'; } if(flag_fu)cout<<'-'; if(flag_jw)cout<<"10"; else if(flag_zero)cout<<'0'; for(i=(s[0]=='0');s[i]!='.';i++)cout<<s[i]; cout<<'.'; for(int j=1;j<=12;j++)cout<<s[i+j]; } ``` - 鸣谢: + 1. [@wangbinfeng](https://www.luogu.com.cn/user/387009) 首先确定本题数据点的错误位置及原因,两次重构本题解(这是第三版题解)。 2. [@N_z_](https://www.luogu.com.cn/user/320087) 指出上一版题解的错误并且给出了本题解使用的正确的做法的思路。 3. 阅读我的题解的大家。 [&animate=true&speed=0.7&color=purple)](https://www.luogu.com.cn/user/387009) $$\color{grey}{\tiny{\texttt{发现上面的签名是动图了吗?}}}$$
正在渲染内容...
点赞
24
收藏
0