动态输出n位小数——满满都是坑!

2024-04-08 22:04
文章标签 输出 动态 小数 满满

本文主要是介绍动态输出n位小数——满满都是坑!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【题目描述】

输入正整数abc,输出a/b的小数形式,精确到小数点后c位。a,b ≤10^6 ,c≤100。输入包含多组数据,结束标记为abc=0。

【样例输入】

1 6 4

0 0 0

【样例输出】

Case 1: 0.1667

【题目来源】

刘汝佳《算法竞赛入门经典  第2版》习题2-5 分数化小数(decimal)

【解析】

这道题很简单呀!

等等,以前都是题目中明确给出保留几位小数,比如要求保留3位小数,咱们就可以:

printf(“%.3f”, a/b);

本题要保留的小数位数是在程序运行时指定的,取决于用户输入的c,这该怎么办呢?难不成要写成这样:

printf(“%.%df”, c, a/b);

实际测试完全行不通,看来格式说明符是不能嵌套的。这其实很好理解,如果支持嵌套,不光看起来乱,还会导致歧义。比如%%df,按理%%应解释为转义字符,用来表示字符“%”。

一、用系统函数动态输出n位小数

1.C语言版

这里涉及一个新的语法知识点,要实现按变量的值动态输出小数位数,就要把代码改成这样子:

printf(“%.*f”, c, a/b);

*和f一样,也是一个占位符,起占位作用,它表示从参数中取一个整数值来作为浮点数的精度。

学会了这种操作,代码马上呼之欲出:

#include<stdio.h>
int main(){int a, b, c, kase=0;while(scanf("%d%d%d", &a, &b, &c)==3 && a*b*c){double f = (double)a/b;printf("Case %d: %.*f\n", ++kase, c, f);}return 0;
}

输入样例测试也满全符合,是不是大功告成了呢?

2. C++版

除了上面的C语言版,也可以使用纯C++语法动态输出小数位数(语法非常麻烦)。

#include <iostream>
#include <iomanip> // 包含 setprecision 的头文件
using namespace std;
int main() {int a, b, c, kase=0;while(scanf("%d%d%d", &a, &b, &c)==3 && a*b*c){double f = (double)a/b;cout << "Case " << ++kase << ": " << fixed << setprecision(c) << f << endl;}return 0;
}

遗憾的是,咱们费了半天劲儿,上面的面代码却都是错误的。

重点就在于题目要求c≤100,如果输入1 6 100,马上就会发现输出结果不正确。

测试是非常重要的,尤其是边界数据的测试更为重要。

因为double的有效精度只有16位,因而用printf根本无法有效输出100位小数。

怎么办呢?自己动手,丰衣足食。

二、自己动手算小数

咱们要用程序模拟小数除法运算,想想小学时学的运算规则:每次求出的余数后添0,然后继续除。比如28÷16:

算法可以描述为:

①求商的整数部分。

②求商的小数部分:重复将余数乘10继续除。

这就完了吗?别忘了“精确到小数点后c位”的意思?最后一位输出的小数需要四舍五入。

代码如下:

#include<stdio.h>
int main(){int a, b, c, kase=0;while(scanf("%d%d%d", &a, &b, &c)==3 && a*b*c){//输出小数点前的内容printf("Case %d: %d.", ++kase, a/b);//输出小数点后的c-1位a=a%b;for(int i=1; i<=c-1; i++){a*=10;printf("%d", a/b);a = a%b; //获取第i位运算后的余数}//输出第c位小数,四舍五入a*=10;printf("%d", a/b + 10*(a%b)/b/5);printf("\n");}return 0;
}

四舍五入部分当然也可以用If语句判断第c+1位小数的值是否大于等于5,这里用了一个巧算,用n/5即可实现当n≥5时,结果为1。

这回终于结束了吧!

No,No,No,客官即将迎来本题的最大深坑。

如果输入下面的数据,就知道上面的代码问题出在哪了。

999 1000 2

9999 1000 2

输出结果:

因为小数部分有一连串的9,进位会导致前面的小数甚至整数全部改变,因而按照上面的代码就会输出错误结果。

如果给咱9999/1000这道算术题,让咱保留两位小数,咱肯定不会算错。但是让咱设计保留2位小数的算法,就非常容易忽略这种情况。前者是看到某种情形,想到对应规则,后者是想到所有情形,想到对应规则,难度不可同日而语。

所以算法设计必须要穷尽所有可能性,遗漏任何一种情形都会导致出错。对于数据来讲,最容易被忽略的就是边界数据。

比如本题,小数位数100位是边界,最后一位小数要四舍五入是边界,9进位会变成0也是边界(9是最大数字)。因此,对于任何类型的数据,一定要养成思考边界、测试边界的习惯。

三、用数组解决999式进位问题

要解决这个问题,就需要用到数组,把每位小数都存到数组中,再根据各位小数值及是否要进位重新修正每位小数的值。

代码如下:

#include<stdio.h>
int main(){int a, b, c, point, d[105], kase=0;while(scanf("%d%d%d", &a, &b, &c)==3 && a*b*c){//求出小数点后的c+1位int r=a%b;for(int i=1; i<=c+1; i++){r*=10;d[i] = r/b;r%=b; //获取第i位运算后的余数}//根据是否进位修正各位小数d[0] = 0; //保存小数到整数的进位if(d[c+1] >= 5){for(int i=c; i>=0; i--){if(9 == d[i]){d[i] = 0;} else{d[i] += 1;break;}}}//输出小数点前的内容printf("Case %d: %d.", ++kase, a/b + d[0]);//输出小数部分for(int i=1; i<=c; i++){printf("%d", d[i]);}printf("\n");}return 0;
}

需要说明的是,本题是第2章习题,此章节还没学到数组,但这道题目前老金只会用数组求解。如有高人知道不用数组的解法,还望指点一二!

最后总结一下,这道题非常容易出错,它有三大神坑:

①用printf函数最多只能输出16位有效小数,无法输出100位,必须要自己模拟除法运算求小数。

②最后一位小数需要根据下一位小数四舍五入。

③最后一位小数如果是9,在四舍五入时会导致前面的小数发生变化,如果有一连串的9,可能会使所有小数甚至整数部分都产生变化。这是本题最大的坑,可谓神之一坑。

这篇关于动态输出n位小数——满满都是坑!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/886521

相关文章

MySQL中动态生成SQL语句去掉所有字段的空格的操作方法

《MySQL中动态生成SQL语句去掉所有字段的空格的操作方法》在数据库管理过程中,我们常常会遇到需要对表中字段进行清洗和整理的情况,本文将详细介绍如何在MySQL中动态生成SQL语句来去掉所有字段的空... 目录在mysql中动态生成SQL语句去掉所有字段的空格准备工作原理分析动态生成SQL语句在MySQL

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

C#如何动态创建Label,及动态label事件

《C#如何动态创建Label,及动态label事件》:本文主要介绍C#如何动态创建Label,及动态label事件,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#如何动态创建Label,及动态label事件第一点:switch中的生成我们的label事件接着,

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

mybatis-plus 实现查询表名动态修改的示例代码

《mybatis-plus实现查询表名动态修改的示例代码》通过MyBatis-Plus实现表名的动态替换,根据配置或入参选择不同的表,本文主要介绍了mybatis-plus实现查询表名动态修改的示... 目录实现数据库初始化依赖包配置读取类设置 myBATis-plus 插件测试通过 mybatis-plu

python多种数据类型输出为Excel文件

《python多种数据类型输出为Excel文件》本文主要介绍了将Python中的列表、元组、字典和集合等数据类型输出到Excel文件中,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一.列表List二.字典dict三.集合set四.元组tuplepython中的列表、元组、字典

基于Canvas的Html5多时区动态时钟实战代码

《基于Canvas的Html5多时区动态时钟实战代码》:本文主要介绍了如何使用Canvas在HTML5上实现一个多时区动态时钟的web展示,通过Canvas的API,可以绘制出6个不同城市的时钟,并且这些时钟可以动态转动,每个时钟上都会标注出对应的24小时制时间,详细内容请阅读本文,希望能对你有所帮助...

Spring AI集成DeepSeek实现流式输出的操作方法

《SpringAI集成DeepSeek实现流式输出的操作方法》本文介绍了如何在SpringBoot中使用Sse(Server-SentEvents)技术实现流式输出,后端使用SpringMVC中的S... 目录一、后端代码二、前端代码三、运行项目小天有话说题外话参考资料前面一篇文章我们实现了《Spring

Vue中动态权限到按钮的完整实现方案详解

《Vue中动态权限到按钮的完整实现方案详解》这篇文章主要为大家详细介绍了Vue如何在现有方案的基础上加入对路由的增、删、改、查权限控制,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、数据库设计扩展1.1 修改路由表(routes)1.2 修改角色与路由权限表(role_routes)二、后端接口设计