一 问题描述
float和double类型不能用于精确计算,其主要目的是为了科学计算和工程计算,它们执行二进制浮点原酸,目的是为了广泛的数值范围上提供较为精确的快速近似计算而精心设计的。但是如果设计钱币之类的计算需要很精确,所以这种情况不能使用float和double,因为要让其精确表示0.1 或者 10的任何负数次方值是不可能的。
二 眼见为实,举例证明:
// float与double 无法精确表示0.1 或者 10的负次方 System.out.println( 0.15 - 0.05 ); // 0.09999999999999999 System.out.println( 2.08f - 3.7f );// -1.6200001
三 举一个现实中会遇到的情况:
一个实际的例子,口袋里面有10元钱,买0.9元的商品十个 , 收银员需要找零1元。计算如下:
10 - 0.9 * 10 = 1代码实现:
float myMoney = 10.00f; float price = 0.9f; for (int i = 0; i < 10; i++) { myMoney -= price; } System.out.println( myMoney ); // 1.0000002
四 解决办法
1. 使用int 和 long类型,如果小数点以后的值可以忽略不计可以只用这两种类型
2. 对计算结果进行四舍五入
3. 使用BigDecimal类型进行浮点运算
先来看一段代码
int intValue = 10; float floatValue = 0.99999f; int count1 = (int) (intValue + floatValue); System.out.println("10 + 0.99999 = " + count1); // output 10 int count2 = (int) (intValue - floatValue); System.out.println("10 - 0.99999 = " + count2); // output 9
更深入的分析可以查看
如何解决float和double误差问题呢?可以使用Java中提供的java.math.BigDecimal,不过此类型是不可变的,每次计算都必须新创建一个对象,不适合大量计算
再来看一个舍入误差会出现的问题
// 10.1 + 0.99*10 = 20 float f1 = 10.1f; float f2 = 0.99f; for (int i = 0; i < 10; i++) { f1 += f2; } System.out.println(f1); // 19.999998
BigDecimal count = new BigDecimal("10.1"); for (int i = 0; i < 10; i++) { count = count.add(new BigDecimal("1")); } System.out.println(count.intValue()); // 20
参考资料:
1. Java 理论与实践: 您的小数点到哪里去了?
2. 《Effective Java》 第2版 第48条 如果需要精确的答案,请避免使用float和double