Tips

书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code

注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。

60. 需要精确的结果时避免使用float和double类型

float和double类型主要用于科学和工程计算。 它们执行二进制浮点运算,经过精心设计,可在很宽的范围内快速提供准确的近似值。 但是,它们不能提供准确的结果,不应在需要确切结果的地方使用。 float和double类型特别不适合进行货币计算,因为不可能将0.1(或任何其他10的负次方)精确地表示为float或double。

例如,假设你的口袋里有1.03美元,花了42美分。 你还剩多少钱? 以下是试图回答这个问题的天真的代码片段:

System.out.println(1.03 - 0.42);

不幸的是,它输出了0.6100000000000001。这不是个例。假设你口袋里有一美元,你买了9垫圈,每个10美分。你还剩多少零钱?

System.out.println(1.00 - 9 * 0.10);

根据这个程序片段,可以得到0.0999999999999999998美元。

你可能认为,只需在打印之前将结果四舍五入就可以解决这个问题,但不幸的是,这种方法并不总是有效。例如,假设你口袋里有一美元,你看到一个货架上有一排好吃的糖果,它们的价格仅仅是10美分,20美分,30美分,以此类推,直到1美元。你每买一颗糖,从10美分的那颗开始,直到你买不起货架上的下一颗糖。你买了多少糖果,换了多少零钱?这里有一个简单的程序设计来解决这个问题:

// Broken - uses floating point for monetary calculation!
public static void main(String[] args) {
double funds = 1.00;
int itemsBought = 0;
for (double price = 0.10; funds >= price; price += 0.10) {
funds -= price;
itemsBought++;
}
System.out.println(itemsBought + " items bought.");
System.out.println("Change: $" + funds);
}

如果你运行该程序,会发现你可以买三块糖果,剩下0.3999999999999999美元。 这是错误的答案! 解决此问题的正确方法是使用BigDecimal,int或long进行货币计算

这里是对上面程序的直接转换,使用BigDecimal类型代替double。 请注意,使用BigDecimal的String类型的构造方法,而不是其double类型构造方法。 这是必要的,以避免在计算中引入不准确的值[Bloch05,Puzzle 2]:

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)) {
funds = funds.subtract(price);
itemsBought++;
}
System.out.println(itemsBought + " items bought.");
System.out.println("Money left over: $" + funds);
}

如果你运行修改后的程序,你会发现可以买到四块糖果,剩下0.00美元。 这是正确的答案。

但是,使用BigDecimal有两个缺点:它没有比使用基本算术类型方便,而且速度要慢得多。 如果你只解决一个简单的问题,后一种缺点是无关紧要的,但前者可能会让你烦恼。

除了使用BigDecimal以外,还可以使用int或long类型,具体取决于所涉及的数量,并自己控制十进制小数点。 在这个例子中,最明显的方法是用美分而不是美元来计算。下面是采用这种方法的简单转换:

public static void main(String[] args) {
int itemsBought = 0;
int funds = 100;
for (int price = 10; funds >= price; price += 10) {
funds -= price;
itemsBought++;
} System.out.println(itemsBought + " items bought.");
System.out.println("Cash left over: " + funds + " cents");
}

总之,对于任何需要精确答案的计算,不要使用float或double。如果希望系统控制十进制小数点,并且不介意不使用基本类型带来的不便和成本,请使用BigDecimal。使用BigDecimal的另一个好处是,它可以完全控制舍入,当执行需要舍入的操作时,可以从八种舍入模式中进行选择。如果你使用合法的舍入行为执行业务计算,这将非常方便。如果性能是最重要的,那么不介意自己控制十进制小数点,而且数量不是太大,可以使用int或long。如果数量不超过9位小数,可以使用int;如果不超过18位,可以使用long。如果数量可能超过18位,则使用BigDecimal。

Effective Java 第三版——60. 需要精确的结果时避免使用float和double类型的更多相关文章

  1. 《Effective Java 第三版》目录汇总

    经过反复不断的拖延和坚持,所有条目已经翻译完成,供大家分享学习.时间有限,个别地方翻译得比较仓促,希望有疑虑的地方指出批评改正. 第一章简介 忽略 第二章 创建和销毁对象 1. 考虑使用静态工厂方法替 ...

  2. 《Effective Java 第三版》新条目介绍

    版权声明:本文为博主原创文章,可以随意转载,不过请加上原文链接. https://blog.csdn.net/u014717036/article/details/80588806前言 从去年的3月份 ...

  3. Effective Java 第三版——10. 重写equals方法时遵守通用约定

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  4. Effective Java 第三版——18. 组合优于继承

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  5. Effective Java 第三版——30. 优先使用泛型方法

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  6. Effective Java 第三版——34. 使用枚举类型替代整型常量

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  7. Effective Java 第三版——1. 考虑使用静态工厂方法替代构造方法

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  8. Effective Java 第三版——3. 使用私有构造方法或枚类实现Singleton属性

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  9. Effective Java 第三版——7. 消除过期的对象引用

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

随机推荐

  1. python 12 模块与包

    一.不知道什么原则 python文件下面只写方法,所有的可直接执行的代码,都放在条件下 原因.该文件有可能被其他文件调用 二.eval() 将字符串转为相应的数据格式可以想象json转为map 三.模 ...

  2. [Hbase]Hbase章4 Hbase分区爆了

    又搞事了,发生了啥事呢:生产分区数暴了,What? 目前的情况: 前提:单Region Server分区上限设置为1000: 目前A表的数据量半年达到25E,20G一分区,达到了900多个分区,这是要 ...

  3. 【机器学习】K均值算法(I)

    K均值算法是一类非监督学习类,其可以通过观察样本的离散性来对样本进行分类. 例如,在对如下图所示的样本中进行聚类,则执行如下步骤 1:随机选取3个点作为聚类中心. 2:簇分配:遍历所有样本然后依据每个 ...

  4. Scara机器人微分运动

    两关节是Scara的典型结构,其运动学.逆运动学及微分运动计算简单,以下以两关节长度相等为条件进行运动学计算演示,l为杆件1,2长度,杆件1起点为基座零点: 一.            正运动学 其中 ...

  5. org.apache.commons.httpclient工具类

    import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler; import org.apache.commons.httpcl ...

  6. ORA-01017 invalid username/password;logon denied

    一般来说这个是密码丢失问题,但也许是因为你的用户名或密码写错啦,所以先检查下用户名和密码,觉得没错的话就去查看 sqlnet.ora (在 安装盘下面的:oracle\product\11.2.0\d ...

  7. MySQL优化:使用show status查看MySQL服务器状态信息

    在网站开发过程中,有些时候我们需要了解MySQL的服务器状态信息,譬如当前MySQL启动后的运行时间,当前MySQL的客户端会话连接数,当前MySQL服务器执行的慢查询数,当前MySQL执行了多少SE ...

  8. php中的问题整理

    1.什么是 CSRF 攻击 ?XSS 攻击?如何防范? CSRF,跨站请求伪造,攻击方伪装用户身份发送请求从而窃取信息或者破坏系统.讲述基本原理:用户访问A网站登陆并生成了cookie,再访问B网站, ...

  9. Python连接oracle数据库的基本操作

    1,创建数据库连接connect和关闭数据库连接close 1.1 创建数据库连接的三种方式: 方法一:用户名.密码和监听分开写 import cx_Oracle db=cx_Oracle.conne ...

  10. orm介绍

    昨日回顾: 1 今日内容: 1 orm介绍 1 tools--->Run manage.py Task python3 manage.py makemigrations 只需要敲命令:makem ...