Java 语言支持两种基本的浮点类型: float
和 double
,以及与它们对应的包装类 Float
和 Double
。它们都依据 IEEE 754 标准,该标准为 32 位浮点和 64 位双精度浮点二进制小数定义了二进制标准。
1 小心!
首先要注意浮点数的就是特殊的浮点值,它们有一些不寻常的特征。例如, 0
和 -0
是不同值,但在比较它们是否相等时,被认为是相等的。用一个非零数去除以无穷大的数,结果等于 0
。特殊数字 NaN 是无序的;使用 ==
、 <
和 >
运算符将 NaN 与其它浮点值比较时,结果为 false
。如果 f
为 NaN,则即使 (f == f)
也会得到 false
。如果想将浮点值与 NaN 进行比较,则使用 Float.isNaN()
方法。
另外在基本 float
类型和包装类 Float
之间,用于比较 NaN 和 -0
的规则是不同的。对于 float
值,比较两个 NaN 值是否相等将会得到 false
,而使用 Float.equals()
来比较两个 NaN Float
对象会得到 true
。造成这种现象的原因是,如果不这样的话,就不可能将 NaN Float
对象用作 HashMap
中的键。类似的,虽然 0
和 -0
在表示为浮点值时,被认为是相等的,但使用 Float.compareTo()
来比较作为 Float
对象的 0
和 -0
时,会显示 -0
小于 0
。
2 使用浮点数的危险:
舍入误差
浮点运算很少是精确的。虽然一些数字(譬如 0.5
)可以精确地表示为二进制(底数 2)小数(因为 0.5
等于 2 -1),但其它一些数字(譬如 0.1
)就不能精确的表示。
比如:
double d = 29.0 * 0.01; System.out.println(d); System.out.println((int) (d * 100));将得到以下输出:
0.29 28
3 浮点数的比较
由于存在 NaN 的不寻常比较行为和在几乎所有浮点计算中都不可避免地会出现舍入误差,所以浮点数的比较运
算解释起来也很麻烦.如果必须比较浮点数来看它们是否相等,则应该将它们差的绝对值同一些预先选定的小正
数进行比较,这样您所做的就是测试它们是否“足够接近”
一种避免 gotcha 的经验法则是显式地测试值的有效性,而不是试图排除无效值。
用if (foo >= 0 && foo < Float.INFINITY) 比用if (foo < 0) 好.因为前者排除了NaN的情况
4 BigDecimal
由于浮点数是不精确的,所以当要表示一些非整数值,特别是涉及到钱这种要很精确的东西时,就不要用浮点数了!
那用浮点数来干什么哩?表示一些测量值之类的数吧,这些数一开始就不怎么准确.
精确的非整数我们用BigDecimal表示,很精确,但缺点是BigDecimal 对象是不可变的,这些方法中的每一个都
会产生新的
BigDecimal
对象。因此,因为创建对象的开销,BigDecimal
不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。
注意:
比较两个BigDecimal 要用
compareTo()
而不是equals()
!!另外在构造时也要小心,
对于
BigDecimal
,有几个可用的构造函数。其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的
String
表示作为输入。要小心使用BigDecimal(double)
构造函数,因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或String
的构造函数。如果使用
BigDecimal(double)
构造函数不恰当,在传递给 JDBCsetBigDecimal()
方法时,会造成似乎很奇怪的 JDBC 驱动程序中的异常。例如,考虑以下 JDBC 代码,该代码希望将数字0.01
存储到小数字段:
PreparedStatement ps = connection.prepareStatement("INSERT INTO Foo SET name=?, value=?"); ps.setString(1, "penny"); ps.setBigDecimal(2, new BigDecimal(0.01)); ps.executeUpdate(); |
在执行这段似乎无害的代码时会抛出一些令人迷惑不解的异常(这取决于具体的 JDBC 驱动程序),因为 0.01
的双精度近似值会导致大的换算值,这可能会使 JDBC 驱动程序或数据库感到迷惑。JDBC
驱动程序会产生异常,但可能不会说明代码实际上错在哪里,除非意识到二进制浮点数的局限性。相反,使用
BigDecimal("0.01")
或 BigDecimal(1, 2)
构造 BigDecimal
来避免这类问题,因为这两种方法都可以
精确地表示小数。
(以上内容详见IBM的developerworks
http://www.ibm.com/developerworks/cn/java/j-jtp0114/index.html)