C语言浮点数比较(floating-point comparisons)的可靠性如何?
浮点数是计算机编程中的“魔法(black art)”,原因之一是没有一种理想的方式可以表示一个任意的数字。电子电气工程协会(IEEE)已经制定出浮点数的表示标准,但你不能保证所使用的每台机器都遵循这一标准。
即使你使用的机器遵循这一标准,还存在更深的问题。从数学意义上讲,两个不同的数字之间有无穷个实数。计算机只能区分至少有一位(bit)不同的两个数字。如果要表示那些无穷无尽的各不相同的数字,就要使用无穷数目的位。计算机只能用较少的位(通常是32位或64位)来表示一个很大的范围内的数字,因此它只能近似地表示大多数数字。
由于浮点数是如此难对付,因此比较一个浮点数和某个值是否相等或不等通常是不好的编程习惯。但是,判断一个浮点数是否大于或小于某个值就安全多了。例如,如果你想以较小的步长依次使用一个范围内的数字,你可能会编写这样一个程序:
#include <stdio.h>
const float first = O.O;
const float last = 70.0
const float small= O.007
main ( )
{
float f;
for (f=first; f !=last && f<last+1.O; f +=small)
printf("f is now %g\n", f);
}
然而,舍入误差(rounding error)和变量small的表示误差可能导致f永远不等于last(f可能会从稍小于last的一个数增加到一个稍大于last的数),这样,循环会跳过last。加入不等式"f<last+1.0"就是为了防止在这种情况发生后程序继续运行很长时间。如果运行该程序并且被打印出来的f值是71或更大的数值,就说明已经发生了这种情况。
一种较安全的方法是用不等式"f<last"作为条件来终止循环,例如:
float f;
for(f=first; f<last; f+=small)
;
你甚至可以预先算出循环次数,然后通过这个整数进行循环计数:
float f;
int count=(last-first)/small;
for(f=first;count-->0;f+=small)
;
即使你使用的机器遵循这一标准,还存在更深的问题。从数学意义上讲,两个不同的数字之间有无穷个实数。计算机只能区分至少有一位(bit)不同的两个数字。如果要表示那些无穷无尽的各不相同的数字,就要使用无穷数目的位。计算机只能用较少的位(通常是32位或64位)来表示一个很大的范围内的数字,因此它只能近似地表示大多数数字。
由于浮点数是如此难对付,因此比较一个浮点数和某个值是否相等或不等通常是不好的编程习惯。但是,判断一个浮点数是否大于或小于某个值就安全多了。例如,如果你想以较小的步长依次使用一个范围内的数字,你可能会编写这样一个程序:
#include <stdio.h>
const float first = O.O;
const float last = 70.0
const float small= O.007
main ( )
{
float f;
for (f=first; f !=last && f<last+1.O; f +=small)
printf("f is now %g\n", f);
}
然而,舍入误差(rounding error)和变量small的表示误差可能导致f永远不等于last(f可能会从稍小于last的一个数增加到一个稍大于last的数),这样,循环会跳过last。加入不等式"f<last+1.0"就是为了防止在这种情况发生后程序继续运行很长时间。如果运行该程序并且被打印出来的f值是71或更大的数值,就说明已经发生了这种情况。
一种较安全的方法是用不等式"f<last"作为条件来终止循环,例如:
float f;
for(f=first; f<last; f+=small)
;
你甚至可以预先算出循环次数,然后通过这个整数进行循环计数:
float f;
int count=(last-first)/small;
for(f=first;count-->0;f+=small)
;