前言

        不论你是不是计算机专业的学生,想必都知道计算机底层的存储结构只能存储0和1.而正是由于这种存储方式,当我们在存储一个浮点数(可以简单的理解为小数)的时候,计算机往往不能准确表达,而能做到的只是近似的表示一个数。

        eg:数字1/5,我们用十进制小数0.2可以准确的表示,而无法用二进制小数准确表示,为了足够精确,我们能做的只有通过增加二进制表示的长度来提高表示的精度。

浮点数运算的特性

eg:

float x, y, z;
	x = 1.0 + 123456.789e30 + -123456.789e30;
	y = 1.0 + 123456.789e30 + (-123456.789e30);
	z = 1.0 + (123456.789e30 + (-123456.789e30));
	printf("x = %f , y = %f , z = %f\n", x, y, z);

这是一段C语言代码,从数学的角度来讲,x、y、z都应该输出1.0才合理,但是运行一下看看

x = 0.000000 , y = 0.000000 , z = 1.000000

 这是为什么呢?

        想要理解这个问题,需要理解浮点数运算在计算机内的执行过程,这里不详细讲解,只是大致概括一下,浮点数的加减法在计算机内大致分为以下几个过程:1、0操作数的判断;2、对阶;3、尾数相加减;4、规格化处理;5、舍入处理;6、判断是否溢出。

        而上述程序的那种现象的出现就与舍入处理这一步有关。

         x和y的输出结果均显示为0是因为:前面1.0+123456.789e30时,会将1.0这个有效数值舍掉,而导致最终结果为0.0

        由于z中括号的存在,会先进行括号里面的而运算,结果是0,然后再与1.0相加。

         同样的,浮点数运算不仅不满足加法的结合律,乘法结合律以及分配律也同样不满足。

拓展 

        在数据结构中往往会计算一个算法的时间复杂度,考虑不细致的话就会默认每一条语句的执行时间差不多,但如果细究起来,其实是不同的,就例如在一个赋值语句中,赋值号右边做了三个加法运算肯定就比一个做了四个加法运算的速度要快,有人说就这么一条语句影响不了什么,确实,但是设想如果在一个循环中存在这么一个语句,那是不是就会产生聚沙成塔,算法效率就会有所影响了呢?所以我们总是会想方设法去提高算法的时间效率。

        eg

float a = 1.0, b = 2.0, c = 3.0, d = 4.0;
	float x, y;
	//优化前
	x = a + b + c;
	y = b + c + d;
	//优化后
	float t = b + c;
	x = a + t;
	y = d + t;

        优化前需要做四次加法运算,而优化后只需要做三次加法运算,效率确实有所提高。但是!经过前一部分的说明,我们意识到在浮点运算的加减法中,舍入处理会给我们带来一些麻烦,因而这种优化是不保守、不安全的。