#每日一题: 浮点型数据的四舍五入格式化输出之谜#

1. Question & Analysis

  1. Question: 最近在牛客网上刷题练手,遇到这样一道题:依次输入一个学生的学号,以及3科(C语言,数学,英语)成绩,在屏幕上输出该学生的学号,3科成绩(注:输出成绩时需进行四舍五入且保留2位小数)。数据范围:学号满足1≤n≤20000000  ,各科成绩使用百分制,且不可能出现负数
  2. Analysis: 我个人觉得,根据题目已知条件,我觉得代码中应该要有判断非法输入的条件语句(这里不符合条件的输入均认为非法输入!走这里就不专门加限制非法输入的条件了,因为这里想要讲的是浮点型数据的四舍五入格式化输出问题)。这里难点就是要搞懂浮点数四舍五入的规则。

2. Code Module

case1: 对 float 类型来说: 可以借助 printf("%.nf", goal_number) 自动四舍五入保留小数点后 n 位小数;

#include <stdio.h>
 
int main(int agrc, const char* agrv[]) {
    int id;
    float c, math, english;
 
    scanf("%d;%f,%f,%f", &id, &c, &math, &english);
 
    printf("The each subject score of No. %d is %.2f, %.2f, %.2f.\n", id, c, math, english);
 
    return 0;
}

case2: 对 double 类型来说,printf("%.nlf", goal_number) 自动四舍五入的规则不明确,感觉有点随机性,我现在还没有弄明白原因!<此处求助一波各位路过的大神帮忙指导或者解释一下> Bug如下:

#include <stdio.h>
 
int main(int agrc, const char* agrv[]) {
    int id;
    double c, math, english;
 
    scanf("%d;%lf,%lf,%lf", &id, &c, &math, &english);
 
    printf("The each subject score of No. %d is %.2lf, %.2lf, %.2lf.\n", id, c, math, english);
 
    return 0;
}

case2-bug

case3: 据说这是通用版的 四舍五入格式化输出浮点数 方法,自己测试过,能用,但不一定是完美的。上代码看看吧 stuck_out_tongue_closed_eyes 假设目标操作数  a = 3.141592654;  如果需要 四舍五入 保留 n 位小数,那么就 (int)(a * 10的 n 次方 + 0.5)/ (10 的 n 次方 * 1.0) , (eg: 保留四位小数输出 a , printf("%.4lf\n", (int)(a * 10000.0 + 0.5) / 10000.0)  ).

#include <stdio.h>
 
int main(int agrc, const char* agrv[]) {
    int id;
    double c, math, english;
 
    scanf("%d;%lf,%lf,%lf", &id, &c, &math, &english);
// 这里备注说明一下,正数则+0.5,负数则-0.5;
    c = (int)(c * 100.0 + 0.5) / 100.0;
    math = (int)(math * 100.0 + 0.5) / 100.0;
    english = (int)(english * 100.0 + 0.5) / 100.0;
 
    printf("The each subject score of No. %d is %.2lf, %.2lf, %.2lf.\n", id, c, math, english);
 
    return 0;
}

3. Summary

虽然,这些都是很弱智的低级问题,但是不弄明白的话,以后用到了,肯定会出Bug的。我始终坚信,Bug都是细节问题演化来的。继续要加油呀!好多天没发贴了。不过并不代表没有学习。发帖其实也蛮费时间的,毕竟要组织语言,排版之类的,哈哈哈(虽然我的帖子也很简化)。发帖就是记录自己的学习和遇到的问题,当然,如果能够得到各路大神的指点,那就是我赚到了,荣幸之至!

168 views
Comments
登录后评论
Sign In
·

找了半天,似乎标准库没有规定舍入的方法,只保证了位数

不过我今天刚好看了IEEE 754的舍入标准,一般使用向偶数舍入的方法,我想你用的编译器就是采用这种方法的,正好对的上,80.555->80.56,90.845->90.84,看和你要保留的最后一位的距离,这样保证了落到两边的概率是50%,在计算概率论的一些东西的时候不会有很大的误差

然后我也在微软的文档找到了相应的说明,printf、_printf_l、wprintf、_wprintf_l | Microsoft Docs

从 Windows 10 版本 2004 (build 19041) 开始, printf 函数系列按用于舍入的 IEEE 754 规则打印完全可表示的浮点数。 在以前版本的 Windows 中,将始终向上舍入以 "5" 结尾的精确表示浮点数。 IEEE 754 指出它们必须舍入到最接近的偶数 (也称为 "银行家舍入" ) 。 例如,和 printf("%1.0f", 2.5) 都 printf("%1.0f", 1.5) 应该舍入为2。 以前,1.5 将舍入为2,2.5 将舍入为3。 此更改只影响精确的可表示数字。 例如,2.35 (当在内存中表示时,) 将继续向上舍入到2.4。 这些函数所做的舍入现在还遵循由设置的 fesetround 浮点舍入模式。 以前,舍入始终选择 FE_TONEAREST 行为。 此更改仅影响使用 Visual Studio 2019 16.2 版和更高版本生成的程序。 若要使用旧的浮点舍入行为,请使用链接 legacy_stdio_float_rounding.obj 。

这就更印证了我认为的没有舍入标准的说法,实际上你要注意有些东西不能先入为主,四舍五入是我们生活中惯用的舍入方法,程序里也不一定表现为四设五入,不过好在你的求知欲帮你“矫正”了

·

计算机中浮点的表示及运算普遍采用IEEE 754标准,而IEEE浮点格式中定义了四种舍入方式。其中向偶数舍入(roune-to-even),也被称为向最接近的值舍入(round-to-nearest),是默认的方式

当出现这样一种情景时,如80.555距离80.55和80.56是相等的,向偶数舍入,即80.56

同理,90.845距离90.84和90.85是相等的,向偶数舍入,即90.84

有什么理由偏向取偶数呢?而不是在中间值的情景向上或向下舍入呢?

很容易假想到这样的情景:若采用向上或向下舍入的方法舍入一组数值,会在计算这些值的平均数中引入统计偏差——舍入后的数字计算出的平均值会比这些数本身的平均值略高或略低一些。而向偶数舍入在大多数现实情况中避免了这种统计偏差