对啊,我也觉得呢。。
下面就有人指出了:
就像我下面会介绍的一样,浮点运算实际上是一个很复杂的东西,首先浮点数并不精确,只是用近似值去代替;
其次浮点运算里面会发生溢出,舍入的情况,导致出现意想不到的结果。
把aaaaaa优化为(aaa)*(aaa)就有可能会出错哦,所以编译器还是采取稳妥一点的思路比较好。
然后还有人补充了,即使是普通的加法运算,编译器也不会利用加法的结合性去优化(这一点下面也提到了),通俗的解释就是“大数吃小数”。
Another similar case: most compilers won't optimize a + b + c + d
to (a + b) + (c + d)
(this is an optimization since the second expression can be pipelined better) and evaluate it as given (i.e. as (((a + b) + c) + d)
). This too is because of corner cases:
float a = 1e35, b = 1e-5, c = -1e35, d = 1e-5; printf("%e %e\n", a + b + c + d, (a + b) + (c + d));
This outputs 1.000000e-05 0.000000e+00
IEEE浮点表示:
使用如下形式:
浮点表示
- 符号s(sign)表示符号
- 有效数significand M是一个二进制小数,它的范围在1-2间或0-1间
- 指数exponent E是2的幂(可以是负数)
浮点数的位被划分为三个域,以编码这些值: - 一个单独的符号s位直接编码s
-
k位的指数域
指数域
编码指数E
-
n位小数域
小数域编码有效数M
在floate中,s,exp,frac分别为 1位,k=8位,n=23位
在double中,s,exp,frac分别为 1位,k=11位,n=52位
c语言中的浮点类型强制转换:
溢出与范围有关,舍入与精度有关
int->float 数字不会溢出,可能被舍入
int/float->double double范围大,所以会被精确的保留
double->float 可能溢出位无穷大,还可能被舍入
float/double->int 值向0截断,比如1.999变为1;还可能会溢出
在Intel处理器的计算机中,由于处理器内部含有寄存器,而浮点寄存器使用特殊的80位扩展精度格式。
这意味着在寄存器中存储一个值,就会在读进读出的时候产生舍入,溢出,数字的值会被改变。
使用浮点运算的时候要小心,因为浮点运算的范围和精度有限,而且浮点运算不遵守普遍的算术属性,比如结合性。