博客更换域名后的第一篇文章。
本周,程序设计基础老师布置了一个上机作业,其中一项是一次生成两个零到九的随机数与加减乘除中的一个运算符号,输出两个数的运算式子,让用户输入运算结果,再把用户输入的值与程序运算的结果比较,从而判断用户运算的结果是否正确。
看到这项作业,我担心遇到无理数的时候如何处理,不可能让人也算到那么多位小数吧……
奇葩的是,老师的给我们的演示截图中恰好没有出现除不尽的情况,不知道是不是特意的?不然我就可以看看老师是偷懒用了整数型还是有高明的算法用浮点型了。
首先想到的是:以数学解决问题
根据多年的人工计算经验,一般都是保留两位小数,因此要求用户输入的数字精确到二位小数即可。
于是手工推导出了判断精确到二位小数后数值正确的条件(字写得不好看,就不拍图了):
-0.005<用户输入值-计算机计算的结果<=0.005
不过缺点也很明显的,你无法阻止用户输入有小于0.005的误差的值。
整数还好解决,强制转换为整数型再相减,结果为0即可判断出用户输入的值是否有误差。
如果是0.550这种,用户输入了0.5519999999999999999999999999……,根据上述推导的判断条件,成立,回答正确。
如何判断用户输入的小数是否多于二位?
于是换了个方向,如何处理计算机的计算结果为保留二位小数后的数值?因为这样的话,就不需要“-0.005<用户输入值-计算机计算的结果<=0.005”这个判断条件了。
如果要手工把浮点型转换为字符串,太麻烦了,假如尾数是1.111,那么总和是1+0.5+0.25+0.125,要实现小数加法……
还是放弃数学方法好了……
平时都用printf(),可以很方便的把各种类型数据格式化为字符串输出,且“.x”修饰符会自动为你四舍五入,既然如此,我可以把格式化输出和格式化输入工作交给C的stdio库了。
printf()是输出到标准输出的,不能用这个,平时用Linux,习惯了数据流重定向,于是想fprintf到stdin,然后scanf stdin,不过很可惜stdin是READ ONLY的。
还有sprintf()与sscanf(),这两个函数直接操作内存,输出和读入的都为字符类型。
到这里,方向明确了,声明一个变量,作为buffer,然后使用sprintf()格式化输出到buffer,再用sscanf()从buffer格式化读入。
最终的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <stdio.h> int main () { char float_string[20]; // 20字节的buffer double num = 5.465/8.69625, user_input; // 声明两个双精度浮点型变量 printf("内部数值: %.9lf\n请输入内部数值保留二位小数后的数值(四舍五入): ", num); scanf("%lf", &user_input); // 读入用户输入的值 sprintf(float_string,"%.2lf", num); // 把内部值以文本形式格式化为两位小数输出到buffer,这里格式化后自动四舍五入 sscanf(float_string, "%lf", &num); // 以文本形式,从buffer中读入内容,格式化为双精度浮点型 printf("输入%s\n", num == user_input ? "正确" : "错误"); return 0; } |
这里还有一个更简单的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <stdio.h> int main () { double num = 5.465/8.69625, user_input; // 声明两个双精度浮点型变量 int num_i; // 声明一个整数型变量 printf("内部数值: %.9lf\n请输入内部数值保留二位小数后的数值(四舍五入): ", num); scanf("%lf", &user_input); // 读入用户输入的值 num_i = num * 1000; // 这样就能取到前三位小数,以整数型储存 num_i += num_i % 10 >= 5 ? 10 : 0; // 对十取余,取得第三位小数,根据第三位小数是否大于等于5而决定是否需要进一 num_i /= 10; // 除十可以去掉第三位小数 num = num_i / 100.; // 这样可以还原为双精度浮点型,好处是不需要改动用户输入的数字,因为一旦把用户输入的数字乘一百,就有可能把用户输入与正确值的误差小于0.01的数当成正确的 printf("输入%s\n", num == user_input ? "正确" : "错误"); return 0; } |