Java 理论与实践: 您的小数点到哪里去了?
使用浮点数和小数中的技巧和陷阱
许多程序员在其整个开发生涯中都不曾使用定点或浮点数,可能的例外是,偶尔在计时测试或基准测试程
序中会用到。Java 语言和类库支持两类非整数类型 ― IEEE 754 浮点( oat 和 double ,包装类
(wrapper class)为 Float 和 Double ),以及任意精度的小数( java.math.BigDecimal )。在本
月的 Java
理论和实践
中,Brian Goetz 探讨了在 Java 程序中使用非整数类型时一些常碰到的陷阱和
“gotcha”。请在本文的 论坛上提出您对本文的想法,以飨笔者和其他读者。(您也可以单击本文顶部或
底部的讨论来访问论坛)。
虽然几乎每种处理器和编程语言都支持浮点运算,但大多数程序员很少注意它。这容易理解 ― 我们中大
多数很少需要使用非整数类型。除了科学计算和偶尔的计时测试或基准测试程序,其它情况下几乎都用不
着它。同样,大多数开发人员也容易忽略 java.math.BigDecimal 所提供的任意精度的小数 ― 大多数
应用程序不使用它们。然而,在以整数为主的程序中有时确实会出人意料地需要表示非整型数据。例如,
JDBC 使用 BigDecimal 作为 SQL DECIMAL 列的首选互换格式。
IEEE 浮点
Java 语言支持两种基本的浮点类型: oat 和 double ,以及与它们对应的包装类 Float 和 Double 。
它们都依据 IEEE 754 标准,该标准为 32 位浮点和 64 位双精度浮点二进制小数定义了二进制标准。
IEEE 754 用科学记数法以底数为 2 的小数来表示浮点数。IEEE 浮点数用 1 位表示数字的符号,用 8 位
来表示指数,用 23 位来表示尾数,即小数部分。作为有符号整数的指数可以有正负之分。小数部分用二
进制(底数 2)小数来表示,这意味着最高位对应着值 ?(2
-1
),第二位对应着 ?(2
-2
),依此类推。对于
双精度浮点数,用 11 位表示指数,52 位表示尾数。IEEE 浮点值的格式如图 1 所示。
图 1. IEEE 754 浮点数的格式
因为用科学记数法可以有多种方式来表示给定数字,所以要规范化浮点数,以便用底数为 2 并且小数点
左边为 1 的小数来表示,按照需要调节指数就可以得到所需的数字。所以,例如,数 1.25 可以表示为尾
数为 1.01,指数为 0: (-1)
0
*1.01
2
*2
0
数 10.0 可以表示为尾数为 1.01,指数为 3: (-1)
0
*1.01
2
*2
3
特殊数字
除了编码所允许的值的标准范围(对于 oat ,从 1.4e-45 到 3.4028235e+38),还有一些表示无穷
大、负无穷大、 -0 和 NaN(它代表“不是一个数字”)的特殊值。这些值的存在是为了在出现错误条件
(譬如算术溢出,给负数开平方根,除以 0 等)下,可以用浮点值集合中的数字来表示所产生的结果。
这些特殊的数字有一些不寻常的特征。例如, 0 和 -0 是不同值,但在比较它们是否相等时,被认为是相
等的。用一个非零数去除以无穷大的数,结果等于 0 。特殊数字 NaN 是无序的;使用 == 、 < 和 >