coollifer 发表于 2013-2-3 14:19:40

Java 理论与实践: 您的小数点到哪里去了?

虽然几乎每种处理器和编程语言都支持浮点运算,但大多数程序员很少注意它。这容易理解 ― 我们中大多数很少需要使用非整数类型。除了科学计算和偶尔的计时测试或基准测试程序,其它情况下几乎都用不着它。同样,大多数开发人员也容易忽略java.math.BigDecimal 所提供的任意精度的小数 ― 大多数应用程序不使用它们。然而,在以整数为主的程序中有时确实会出人意料地需要表示非整型数据。例如,JDBC 使用 BigDecimal 作为 SQL DECIMAL 列的首选互换格式。
浮点中的危险
由于无穷大、NaN 和 0 的特殊行为,当应用浮点数时,可能看似无害的转换和优化实际上是不正确的。例如,虽然好象 0.0-f 很明显等于 -f ,但当 f 为 0 时,这是不正确的。还有其它类似的 gotcha,表 2 显示了其中一些 gotcha。
浮点数比较指南
由于存在 NaN 的不寻常比较行为和在几乎所有浮点计算中都不可避免地会出现舍入误差,解释浮点值的比较运算符的结果比较麻烦。
最好完全避免使用浮点数比较。当然,这并不总是可能的,但您应该意识到要限制浮点数比较。如果必须比较浮点数来看它们是否相等,则应该将它们差的绝对值同一些预先选定的小正数进行比较,这样您所做的就是测试它们是否“足够接近”。(如果不知道基本的计算范围,可以使用测试“abs(a/b - 1) < epsilon”,这种方法比简单地比较两者之差要更准确)。甚至测试看一个值是比零大还是比零小也存在危险 ―“以为”会生成比零略大值的计算事实上可能由于积累的舍入误差会生成略微比零小的数字。
NaN 的无序性质使得在比较浮点数时更容易发生错误。当比较浮点数时,围绕无穷大和 NaN 问题,一种避免 gotcha 的经验法则是显式地测试值的有效性,而不是试图排除无效值。在清单 1 中,有两个可能的用于特性的 setter 的实现,该特性只能接受非负数值。第一个实现会接受 NaN,第二个不会。第二种形式比较好,因为它显式地检测了您认为有效的值的范围。

用于较小数的 BigDecimal</p>从 JDK 1.3 起,Java 开发人员就有了另一种数值表示法来表示非整数: BigDecimal 。 BigDecimal 是标准的类,在编译器中不需要特殊支持,它可以表示任意精度的小数,并对它们进行计算。在内部,可以用任意精度任何范围的值和一个换算因子来表示BigDecimal ,换算因子表示左移小数点多少位,从而得到所期望范围内的值。因此,用 BigDecimal 表示的数的形式为unscaledValue*10 -scale 。
用于加、减、乘和除的方法给 BigDecimal 值提供了算术运算。由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的BigDecimal 对象。因此,因为创建对象的开销, BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。如果您正在寻找一种能精确表示如货币量这样的数值,则 BigDecimal 可以很好地胜任该任务。
结束语
在 Java 程序中使用浮点数和小数充满着陷阱。浮点数和小数不象整数一样“循规蹈矩”,不能假定浮点计算一定产生整型或精确的结果,虽然它们的确“应该”那样做。最好将浮点运算保留用作计算本来就不精确的数值,譬如测量。如果需要表示定点数(譬如,几美元和几美分),则使用 BigDecimal 。

参考资料

[*]您可以参阅本文在 developerWorks 全球站点上的 英文原文. 
[*]请参与本文的 论坛。(您也可以单击本文顶部或底部的 讨论来访问论坛)。 
[*]David Goldberg 的经典文章 What Every Scientist Should Know About Floating-Point Arithmetic对各种浮点表示进行了权衡,并讨论了它们各自的缺点。 
[*]IBM Hursley 实验室的 FAQ探讨了有关小数运算的一些问题。 
[*]Bill Venners 在 JavaWorld 的 Under the hood 专栏(Bill Venners 撰写)研究了 JVM 中的浮点支持。 
[*]IEEE 754 的首席架构设计师之一 William Kahan 在“ How Java's Floating-Point Hurts Everyone Everywhere”(PDF)一文中批评了不完整的 Java 浮点实现。 
[*]Intel Architecture Software Developer's Manual(PDF)详细讲述了 IEEE 754 浮点的最常见实现。 
[*]Castor XML 数据绑定框架使用 BigDecimal 作为小数的互换类型。 
[*]Java Developer Connection 的 Tech Tips就 Java 2 平台中数字计算提供了一些指导。 
[*]JavaWorld文章“ Make cents with BigDecimal”为使用 BigDecimal 和 NumberFormat 来进行一些财会计算提供了一些有意义的技巧。 
[*]阅读 Java theory and practice 专栏 (Brian 撰写)中的所有文章。 
关于作者
<div style="margin-top: 0px; margin-right: 0px; margin-bottom: 1.2em; margin-left: 0px; width: 710px; border-top-width: 1px; border-top-style: solid; border-top-color: #cccccc; font-family: Simsun; text-align: left; font-size: medium;" class="ibm-container ibm-portrait-module ibm-alternate-two"><div style="border-bottom-color: #cccccc; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: #cccccc; border-left-style: solid; border-left-width: 1px; border-right-color: #cccccc; border-right-style: solid; border-right-width: 1px; background-position: 0px 100%;" class="ibm-container-body">Brian Goetz 是一位软件顾问,在过去的 15 年里一直是一位专业软件开发人员。他是 Quiotix的首席顾问,该公司是位于加利福尼亚 Los Altos 的软件开发和咨询公司。请在业界流行的出版物上查阅 Brian 已发表的和即将发表的文章。 可以通过brian@quiotix.com与 Brian 联系。
 
转载出处:http://www.ibm.com/developerworks/cn/java/j-jtp0114/index.html
页: [1]
查看完整版本: Java 理论与实践: 您的小数点到哪里去了?