随笔 - 9  文章 - 21  trackbacks - 0
<2008年5月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

常用链接

留言簿(1)

随笔分类(9)

随笔档案(9)

搜索

  •  

最新评论

阅读排行榜

评论排行榜

第 2 天的问题

请考虑下面这段话所描述的问题:

小明在超市购买了一个价值¥1.10的东西,但是他钱包中只有两元一张的钞票。如果他用一张两元的钞票支付,那么应该找给他多少零钱呢?

下面是一个试图解决上述问题的程序,它会打印出什么呢?


public class Change{
    public static void main(String args[]){
        System.out.println(2.00 - 1.10);
    }
}

第 2 天问题的解答

你可能会说,这不是很简单嘛!输出0.90。但实际的输出确实:0.8999999999999999。

原因

一、程序如何才能知道你想要打印小数点后两位小数呢?

JDK API文档中关于Double.toString的部分指出:

Double.toString的返回值,是足以将double类型的值与最靠近它的临近值区分出来的最短的小数,它在小数点之前和之后都至少有一位。

因此,看起来,该程序应该打印0.9是合理的。

二、并不是所有的小数都可以用二进制浮点数来精确表示的。

问题在于1.1这个数字不能被精确表示成为一个double,因此它被表示成为最接近它的double值。 该程序从2中减去的就是这个值。 遗憾的是,这个计算的结果并不是最接近0.9的double值。 表示结果的double值的最短表示就是你所看到的打印出来的那个可恶的数字。

第 2 天问题的解决办法

方法一


//拙劣的解决方案--仍旧是使用二进制浮点数
System.out.printf("%.2f%n",2.00 - 1.10);

这条语句打印的是正确的结果,但是这并不表示它就是对底层问题的通用解决方案。它使用的仍旧是二进制浮点数的double运算。 浮点运算在一个范围很广的值域上提供了很好的近似,但是它通常不能产生精确的结果。 二进制浮点对于货币计算是非常不适合的,因为它不可能将0.1--或者10的其它任何次负幂--精确表示为一个长度有限的二进制小数

方法二

System.out.println((200 - 110) + "分");

这是在Java中一种简单的解决方法。在Java标准库中,有一个类似的范例Date类,Date类的对于时间的内部表示就是一个以毫秒为单位的long整数。 对于像货币这样的数值,可以用int或long来表示。如果你采纳了解决方法,请确保该整数类型大到足够表示在程序中你将要用到的所有值

方法三


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")));
    }
}

解决该问题的另一种方式是使用执行精确小数运算的BigDecimal。 这里要告诫你一点: 一定要用BigDecimal(String)构造器,而千万不要用BigDecimal(double)。 后一个构造器将用它的参数的"精确"值来创建一个实例:new BigDecimal(1.1)将返回一个表示1.100000000000000055511151231257827021181583404541015625 的BigDecimal。

这个版本并不是十分地完美,因为Java并没有为BigDecimal提供任何语言上的支持。原因:

  • 使用BigDecimal的计算很有可能比那些使用原始类型的计算要慢一些,对某些大量使用小数计算的程序来说,这可能会成为问题,而对大多数程序来说,这显得一点也不重要。
  • Java不允许运算符重载。使得代码不够直观。

总结

在需要精确答案的地方,要避免使用float和double。

对于货币计算,要使用int、long或BigDecimal。

对于语言设计者来说,应该考虑对小数运算提供语言支持。可以支持下列方式中的一种。

  • 提供对操作符重载的有限支持, 以使得运算符可以被塑造为能够对数值引用类型起作用, 例如BigDecimal。
  • 提供原始的小数类型,就像 COBOL 与 PL/I 所作的一样。

今天的问题

被除数表示的是一天里的微秒数;而除数表示的是一天里的毫秒数。这个程序会打印出什么呢?


public class LongDivision{
    public static void main(String args[]){ 
        final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000; 
        final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000; 
        System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY); 
    } 
} 

posted on 2008-05-16 22:50 李四飞刀 阅读(1293) 评论(0)  编辑  收藏 所属分类: 每日一题

只有注册用户登录后才能发表评论。


网站导航: