float和double类型的主要设计目的是为了科学计算和工程计算。它们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供彻底精确的结果,因此不该该被用于要求精确结果的场合。由于要让一个float或者double精确地表示0.1(或者10的任何负数次方值)是不可能的,float和double类型对于货币计算尤其不合适,由于要让一个float或double精确地表示0.1或10的任何其余负数次方值是不可能的。好比System.out.println(2.0-1.1)将会打印0.899999999999999,而不是你所但愿的0.9,这种舍入错误产生的缘由是浮点数其实是用二进制系统实现的,而分数1/10在二进制系统中没有精确的表示,其道理就如同在十进制系统中没法精确表示1/3同样;再好比0.5在二进制系统中有精确表示,而0.55在二进制系统中没有精确表示。 java
System.out.println(1.03 – .42); System.out.println(1.00 – 9 * .10);
输出结果为:
0.6100000000000001
0.09999999999999998 sql
你可能会认为,只要在打印以前将结果作一下舍入就能够解决这个问题,但遗憾的是,这种作法并不老是可行。
例如:假设你口袋里有1美圆,你看到货架上有一排糖果,标价分别是10美分、20美分、30美分等等,一直到1美圆。你打算从标价为10美分的开始买,每种买一颗,直到不能支付货架上的任何一种价格为止,那么你能够买多少颗糖果?还会找回多少零头?下面是一个简单的程序,用来解决这个问题: 性能
double funds = 1.00; int itemsBought = 0; for (double price = .10; funds >= price; price += .10) { funds -= price; itemsBought++; } System.out.println(itemsBought + " items bought."); System.out.println("Change: $" + funds);
若是运行这个程序你会发现你能够支付3颗糖果,而且还剩0.3999999999999999美圆。显然这个答案是不正确的。解决这个问题的正确办法是使用BigDecimal、int或long进行货币计算。 spa
下面看看使用BigDecimal类型代替double的程序: 设计
final BigDecimal TEN_CENTS = new BigDecimal(".10"); int itemsBought = 0; BigDecimal funds = new BigDecimal("1.00"); for (BigDecimal price = TEN_CENTS; funds.compareTo(price) >= 0; price = price .add(TEN_CENTS)) { itemsBought++; funds = funds.subtract(price); } System.out.println(itemsBought + " items bought."); System.out.println("Money left over: $" + funds);
输出结果0.00和4 这样就正确了
code
关于BigDecimal的方法咱们下去再仔细看看;
这里要说的是BigDecimal是引用数据类型 ci
总而言之: 对于任何要精确答案的计算任务,请不要用double和float。若是你想 计算十进制小数点,而且不介意非基本类型带来的不便,那就用BigDecimal;使用 BigDecimal还有个额外的好处就是他容许你彻底控制舍入,每当一个操做涉及舍入的时候 它容许你从8种舍入模式中选择一种。若是你正确经过法定要求的舍入行为进行业务计算使用BigDecimal时很是方便的;若是性能十分关键,而且你又不介意本身记录十进制的 小数点,并且涉及数值不大,就可使用int或者long; 若是数值范围没有超过9位的十进制数字,能够用int;若是数值范围没有超过18位的十进制数字,用long 若是范围超过了18位数,那咱们必须使用BigDecimal。 it