计算机中浮点数的舍入
学过计算机组成原理的同学都知道在计算机中,浮点数是通过3个部分来表征的,1个bit的符号位,k个bit的指数位和n个bit的有效数位,对于C语言中的float和double,k、n值分别是8、23和11、52。
我们可以想象一下,表征浮点数的bit是有限的,因而其组合也是有限的,是无法完全表征所有实数的,事实上,连一些10进制下看上去很简单的数也都无法精确表示。比如,你可以试一下下面几行代码:
1 2 3 4 5 6 7 | #include <stdio.h> int main() { double a = 0.1; printf("%.20lf",a); return 0; } |
你会看到输出的其实是0.10000000000000001000。
正是由于很多实数无法精确表示,所以在计算机处理浮点数的时候,需要进行大量的舍入操作,那么计算机默认是采用什么样的舍入策略呢?
以前我一直以为计算机采用的就是人们日常所熟悉的“四舍五入”法则,因为在printf函数中,如果指定输出精度小于实际精度的话,输出的结果就是按四舍五入产生的。其实这是printf函数故意设计成这样以符合人们的日常习惯的。
实际计算机中,当需要进行舍入操作时,运用是一种叫“round-to-even”的策略,即“向偶数舍入”,举个简单的例子就可以明白了,比如1.235和1.245,舍入后都是1.24, 也就是说要使得舍入后的最后一位有效数字是偶数。
为什么要采用这样的策略,而不直接使用我们已经习惯的“四舍五入”呢。原因在于,在进行舍入的时候,最后一位数字从1到9, 舍去的有1,2,3,4;正好可以和进位的9,8,7,6相对应,而5却被单独出来了,如果我们采用四舍五入每次都将5进位的话,在进行一些大量数据的统计时,就会累积比较大的偏差,而如果采用”round-to-even”的策略,在巨大多数情况下,5舍去还是进位的几率差不多,统计时产生的偏差也就相应要少一些了。
其实这是printf函数故意设计成这样以符合人们的日常习惯的。 好像不是四舍五入吧。”%.2f”多试试。 囧
是四舍五入。囧 精度问题。
int main() {
printf(“%.20lf %.2f\n”, 0.015, 0.015); // 0.01499999999999999900 0.01
printf(“%.20lf %.2f\n”, 0.025, 0.025); // 0.02500000000000000100 0.03
printf(“%.20lf %.2f\n”, 0.035, 0.035); // 0.03500000000000000300 0.04
printf(“%.20lf %.2f\n”, 0.045, 0.045); // 0.04499999999999999800 0.04
printf(“%.20lf %.2f\n”, 0.055, 0.055); // 0.05500000000000000000 0.06
printf(“%.20lf %.2f\n”, 0.065, 0.065); // 0.06500000000000000200 0.07
printf(“%.20lf %.2f\n”, 0.075, 0.075); // 0.07499999999999999700 0.07
printf(“%.20lf %.2f\n”, 0.085, 0.085); // 0.08500000000000000600 0.09
printf(“%.20lf %.2f\n”, 0.095, 0.095); // 0.09500000000000000100 0.10
return EXIT_SUCCESS;
}