通讯原理考完,终于有空结束第二章。
首先来看这样一个例子。
double recip(int denom)
{
return 1.0/(double) denom;
}
void do_nothing() {} /* Just like name say */
void test1(int denom)
{
double r1, r2;
int t1, t2;
r1 = recip(denom); /* Stored in memory */
r2 = recip(denom); /* Stored in register */
t1 = r1 == r2; /* Compares register to memory */
do_nothing(); /* Forces register save to memory */
t2 = r1 == r2; /* Compares memory to memory */
printf ("test1 t1: r1 %f %c= r2 %f \n", r1, t1 ? '=' : '!', r2);
printf ("test1 t2: r1 %f %c= r2 %f \n", r1, t2 ? '=' : '!', r2);
}
上面一段代码的内容是很明确的,函数recip进行直接的倒数,函数do_nothing确实什么也没干。
而且,变量t1,t2都是由 r1==r2 计算出来,所以我们预计他们是相等的。
然而,当带优化选项”-o2″编译,并用参数10运行时,我们得到:
test1 t1: r1 0.100000 != r2 0.100000
test2 t2: r1 0.100000 == r2 0.100000
优化编译器的基本原则是,无论优化与否,程序应当产生完全相同的结果,
不幸的是,GCC对IA32机器上的浮点代码没有满足这一要求。
小注解: 研究机器级代码(如汇编),对于程序员的进阶常常是很有必要的。仔细观察代码段的注释,
在两次比较中,由于优化选项的存在,
第一次的比较的两个单位会是扩展精度(特殊的80位浮点寄存器)与双精度(double),
而第二次则是两个双精度。
存在这样的杯具是因为一段时间内编译器把r2存放在临时的浮点寄存器
此外,大的浮点数转换成整数是一种常见的错误来源,Ariane 5号火箭初次航行的浮点数错误,
仅仅升空37秒便爆炸掉5亿美元的通讯卫星。当然错误来源复杂的多,其根源确实是浮点数转换时未仔细处理的问题。
(在早期工作人员设计Ariane 4时设计16位整数,而5号的浮点运算发现转到16位会产生溢出,于是系统返回了溢出模式,而不是继续控制。)
谨慎操作浮点数