您的位置:首页 > Web前端

Effective Java(2nd Edition) Item 48 如果需要精确结果,请避免使用float与double(译文)

2009-01-07 14:30 579 查看
设计float与double类型主要用于科学与工程计算。它们用于二进制浮点运算,二进制浮点运算被仔细设计,可在一个很宽的数量级范围内提供足够精确的近似计算。然而它们不能提供精确的结果,当需要精确的结果时就不应使用它们。Float与double类型特别不适合于货币计算,因为不可能用float或double精确的表示0.1(及任何10的负幂次方的数)。
       例如,假设你手头有$1.03,你花了42¢,你还剩多少钱呢?下面是解答这个问题的最自然的代码片断:
       System.out.println(1.03 - .42);
不幸的是,它的输出是0.6100000000000001。这并非一个孤立现象。假设你手头有一美元,你买了9只垫圈,每只10美分,还剩多少钱呢?
       System.out.println(1.00 - 9 * .10);
如果按这段代码,你得$0.09999999999999998.
       你可能会认为只要在打印前先四舍五入就可以解决这个问题,不幸的是,这并非总是可行的。例如,假设你手头有1美元,贷架上放着一排精美的糖果,标价为10美分、20美分、30美分,如些值到1美元。你想买每颗这样的糖果,从10美分开始,值到余下的钱不够买下一颗为止。你能买几颗糖呢?还留下多少钱呢?下面是一个解决这个问题的自然代码段。
// Broken - uses floating point for monetary calculation!<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

public static void main(String[] args) {
       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);
}
如果你运行这段代码,你可以发现你能买到三颗糖,剩下$0.3999999999999999。这是一个错误的答案。解决这个问题的正确做法是在货币计算中使用BigDecimal、int或long
       下面的程序段在前面的程序中用BigDecimal直接替换了double。
public static void main(String[] args) {
       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。这是正确的答案。
       然而使用BigDecimal也有两点劣势,它没有基本算术类型方便,而且比较慢。如果你解决的是单个小问题,比较慢就不成问题。但前面这个劣势可能会让你不舒服。
       可依据所处理的数量大小,使用int或long来代替BigDecimal,并由自已跟踪处理小数点位。在上面的例子中,一个显然的方法是使用美分而不是美元来进行所有的运算。下面的程序展示了这个方法。
       public static void main(String[] args) {
              int itemsBought = 0;
              int funds = 100;
              for (int price = 10; funds >= price; price += 10) {
                     itemsBought++;
                     funds -= price;
              }
              System.out.println(itemsBought + " items bought.");
              System.out.println("Money left over: "+ funds + " cents");
       }
       总之,不要将float或double用于需要精确结果的计算。如果你希望系统跟踪处理小数点,并且不介意不使用基本类型的不便与花销,就使用BigDecimal。使用BigDecimal还有一个额外的好处是你可以完全控制四舍五入,允许你只要有小数尾数,就可以选择八种四舍五入模式之一进行操作。如果你所运行的业务运算要求有特别地四舍五入行为,这会带来很大便利。如果性能是关键的,而且你也不介意自已跟踪处理小数点位,数值也不特别大,就使用int或long。如果数值不超过十进制9位数,你可以使用int,如果数值不超过18位数,你可以使用long。如果数值可能超过18位,你就得使用BigDecimal。
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  float java string