Java Puzzlers 之Puzzle 2: Time for a Change
来源:互联网 发布:recyclerview数据错乱 编辑:程序博客网 时间:2024/05/16 01:52
Puzzle 2: Time for a Change
考虑下面的问题:
Tom去汽车零件店去买价值1.10元的火花塞,但是在他的钱包都是2美元的票子,如果他用两美元的票子买那个火花塞,那么最后能给他找回的零钱是多少呢?
使用用以下的程序来实现,输出的结果是多少呢?
public class Change {
public static void main(String args[]) {
System.out.println(2.00 - 1.10);
}
}
Solution 2: Time for a Change
很自然的,你可能认为程序输出的结果就是0.9, 但是程序怎么知道你想在小数点后面输出两位小数呢?如果你知道关于double转化为字符串的相关规则(在java-API中,有相关文档详细说明了Double.toString)的话,你可以了解到程序打印出足够短的小数去从它最近的相邻的值区别double类型的值,这个数至少一位小数。看起来很合乎情理,那么,程序的输出应该就是0.9 。合乎情理,有时也可能不对。如果你运行此程序,你就会发现结果是0.8999999999999999。
问题在于数字1.1 不能正确表示为double,因此它用最接近的double值来表示。在程序中用2减去这个值。不幸的是,结果并不是最接近0.9的double值。 最接近结果的值就是在运行上面程序后输出的可怕的数字。
通常情况下,问题在于:并不是所有的数字都可以使用浮点数来完全正确表示。如果你使用5.0以及其后的版本,你就很轻松的使用printf来修正这个错误,能得到完全精确的值:
// Poor solution - still uses binary floating-point!
System.out.printf("%.2f %n", 2.00 - 1.10);
输出的结果正确,但是并不代表从根本上解决问题:它仍然使用double二进制浮点数算法。浮点数算法提供了很好的广域的值匹配而不是完全的精确结果。二进制浮点计算是很不适合于钱方面的计算。因为它不可能表示0.1或者其他的10的负数幂(特别是有限二进制小数)。
解决方法之一就是使用整数,如int 或者long,使用美分来进行结算。如果使用这样的结算方法,首先必须确保你的整数足够大,以致于能表示这些数。对于上面这个问题,整数完全足够。现在我们使用println来重新结算,使用int值来表示美分值得运算,这个版本中,输出的就是90美分,这个结果就是正确的:
System.out.println((200 - 110) + " cents");
另外一个解决的办法就是使用BigDecimal,这个可以进行精确的计算。这个也是SQL中的DECEMAL类型经由JDBC转为而成的类型。这里有一个警告:总是使用BigDecimal(String)构造函数,而不要使用BigDecimal(double)。后者将会创建参数的一个“准确”的值:new BigDecimal(.1)将会返回0.1000000000000000055511151231257827021181583404541015625。正确的使用BigDecimal,程序就会输出正确的结果:0.9。
import java.math.BigDecimal;
public class Change {
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/L那样提供原始小数类型。
- Java Puzzlers 之Puzzle 2: Time for a Change
- Java Puzzlers笔记--Puzzle 2: Time for a change 关于浮点型的问题
- Java Puzzlers 之Puzzle 1: Oddity
- Java Puzzlers 之Puzzle 3: Long Division
- Puzzel2:Time for a Change
- Java Puzzlers 之Puzzle 4: It's Elementary
- Java Puzzlers笔记--puzzle 10: Tweedledee += 问题(2)
- 谜题2:Time for a change 找零时刻
- Java Puzzlers(2)字符串之谜
- Java Puzzlers(2)字符串之谜
- Java Puzzlers笔记--puzzle 21: What's my Class, take 2 路径字符问题
- Java Puzzlers笔记--Puzzle 1: Oddity奇偶问题
- Java Puzzlers笔记--Puzzle 3: Long Division 溢出问题
- Java Puzzlers笔记--puzzle 6: Multicast 类型转换
- Java Puzzlers笔记--puzzle 7: Swap Meat ^符号问题
- Java Puzzlers笔记--puzzle 9: Tweedledum +=的问题
- Java Puzzlers笔记--puzzle 14: Escape Rout (")双引号的问题
- Java Puzzlers笔记--puzzle 17: Huh? 转义字符问题
- AWP之使用初步
- js写的简单宠物程序
- 使用W3C DOM 和 JAXP 来建立和输出DOM文档并设置文档参数的方法
- 每天进步一点.十天也有一大步
- JavaScript对象之数组Array
- Java Puzzlers 之Puzzle 2: Time for a Change
- 数据窗口修改小技巧
- Two Methods to Remove Duplicates in an ArrayList
- 无法解决 equal to 操作的排序规则冲突
- JavaScript对象之Boolean
- .NET读取QQWry.Dat 纯真版ip数据库格式数据源
- Intel PXA27x平台中的UART接口驱动
- Java容器 HashMap
- Win32下快速建立Subversion服务器