编辑: AA003 | 2013-04-07 |
2 中减去的就是这个值.遗憾的是,这个计算的 结果并不是最接近 0.9 的double 值. 表示结果的 double 值的最短表示就是你所 看到的打印出来的那个可恶的数字. 更一般地说,问题在于并不是所有的小数都可以用二进制浮点数来精确表示的. 如果你正在用的是 JDK 5.0 或更新的版本,那么你可能会受其诱惑,通过使用 printf 工具来设置输出精度的方订正该程序: //拙劣的解决方案――仍旧是使用二进制浮点数 System.out.printf( %.2f%n ,2.00 - 1.10);
这条语句打印的是正确的结果, 但是这并不表示它就是对底层问题的通用解决方 案:它使用的仍旧是二进制浮点数的 double 运算.浮点运算在一个范围很广的 iTePub.Net-Collect 值域上提供了很好的近似,但是它通常不能产生精确的结果.二进制浮点对于货 币计算是非常不适合的, 因为它不可能将 0.1――或者
10 的其它任何次负幂―― 精确表示为一个长度有限的二进制小数 解决该问题的一种方式是使用某种整数类型,例如 int 或long,并且以分为单 位来执行计算.如果你采纳了此路线,请确保该整数类型大到足够表示在程序中 你将要用到的所有值. 对这里举例的谜题来说, int 就足够了. 下面是我们用 int 类型来以分为单位表示货币值后重写的 println 语句. 这个版本将打印出正确答 案90 分: System.out.println((200 - 110) + cents );
解决该问题的另一种方式是使用执行精确小数运算的 BigDecimal.它还可以通 过JDBC 与SQL DECIMAL 类型进行互操作.这里要告诫你一点: 一定要用 BigDecimal(String)构造器,而千万不要用 BigDecimal(double).后一个构造 器将用它的参数的 精确 值来创建一个实例:new BigDecimal(.1)将返回一个 表示 0.100000000000000055511151231257827021181583404541015625 的BigDecimal.通过正确使用 BigDecimal,程序就可以打印出我们所期望的结果 0.90: import java.math.BigDecimal;
public class Change1{ public static void main(String args[]){ System.out.println(new BigDecimal( 2.00 ). subtract(new BigDecimal( 1.10 )));
} } 这个版本并不是十分地完美, 因为 Java 并没有为 BigDecimal 提供任何语言上的 支持.使用 BigDecimal 的计算很有可能比那些使用原始类型的计算要慢一些, 对某些大量使用小数计算的程序来说, 这可能会成为问题, 而对大多数程序来说, 这显得一点也不重要. 总之, 在需要精确答案的地方,要避免使用 float 和double;
对于货币计算, 要使用 int、long 或BigDecimal.对于语言设计者来说,应该考虑对小数运算 提供语言支持.一种方式是提供对操作符重载的有限支持,以使得运算符可以被 塑造为能够对数值引用类型起作用,例如 BigDecimal.另一种方式是提供原始 的小数类型,就像 COBOL 与PL/I 所作的一样. 谜题 谜题 谜题 谜题
3 3
3 3: : : :长整除 长整除 长整除 长整除 这个谜题之所以被称为长整除是因为它所涉及的程序是有关两个 long 型数值整 除的.被除数表示的是一天里的微秒数;
而除数表示的是一天里的毫秒数.这个 程序会打印出什么呢? public class LongDivision{ public static void main(String args[]){ final long MICROS_PER_DAY =
24 *
60 *
60 *
1000 * 1000;
iTePub.Net-Collect final long MILLIS_PER_DAY =
24 *
60 *
60 * 1000;
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
} } 这个谜题看起来相当直观. 每天的毫秒数和每天的微秒数都是常量. 为清楚起见, 它们都被表示成积的形式.每天的微秒数是(24 小时/天*60 分钟/小时*60 秒/ 分钟*1000 毫秒/秒*1000 微秒/毫秒).而每天的毫秒数的不同之处只是少了最 后一个因子 1000. 当你用每天的毫秒数来整除每天的微秒数时,除数中所有的因子都被约掉了,只 剩下 1000,这正是每毫秒包含的微秒数. 除数和被除数都是 long 类型的,long 类型大到了可以很容易地保存这两个乘积 而不产生溢出.因此,看起来程序打印的必定是 1000. 遗憾的是,它打印的是 5.这里到底发生了什么呢? 问题在于常数 MICROS_PER_DAY 的计算 确实 溢出了.尽管计算的结果适合放 入long 中,并且其空间还有富余,但是这个结果并不适合放入 int 中.这个计 算完全是以 int 运算来执行的,并且只有在运算完成之后,其结果才被提升到 long, 而此时已经太迟了: 计算已经溢出了, 它返回的是一个小了