3.4.3 浮点值的比较
因为浮点值只是近似,所以即使是最简单的浮点操作都有可能出错。例如如下表达式:
if (x == y) {...}
这个表达式是真的表示两个浮点数完全相等吗?或者是表示这两个数近似相等吗(这种情况下可以使用≌这个符号)?有没有两个值虽然不同,但是相差非常小却仍然被认为是相等的呢?我们来看个例子,笛卡儿坐标系上有三个点,p0 = (a, b)、p1 = (c, d)和p2 = (e, f),表示两个有向线段(p0, p1)和(p1, p2)。我们能够使用(c - a)(f - b) - (d - b)(e - a)这个式子来计算这两条线段是否共线(也就是在同一条线上)。如果这个式子的结果:
为了展示了Java 浮点计算中误差是如何产生的考虑使用表3-3中a~f的值定义3个点。
表3-3:浮点算术误差
可以很明显地看出,这p0、p1、p2三个点其实是共线的,而且直线方程是y = 5*x。当使用浮点数计算判断线段的共线性时,浮点运算中的固有误差影响了浮点运算的结果。使用32位单精度浮点数的时候,计算结果是0.00048828125。而使用双精度浮点数(64位),计算出来的结果却是一个很小的负数!这个例子生动地展现了无论是32位还是64位浮点数,它们都无法准确地表示计算出实际的数值,因此无法确定这两条线段是否共线。这就是浮点世界的常态。
常见的一种解决办法是引入一个非常小的值δ,来决定两个浮点数是否存在≌(近似相等)关系。如果|x - y|<δ,那么认为x和y是相等的。但是,即便这样,如果存在x≌y和y≌z,但是x≌z可能并不为真。这不但违背了数学运算的传递性,而且增加了编写正确代码的难度。而且这种办法并没有解决共线性问题,因为共线性问题的确定用的是值的符号(0,正,负)而不是相等与否。