第 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) 编辑 收藏 所属分类:
每日一题