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. SpringBoot对注册用户密码进行Bcrypt密码加密

    一.注册用户时,用户的密码一般都是加密存储在数据库中.今天我要用到的加密方式是Bcrypt加密. 1.首先在SpringBoot项目的pom文件中,引入SpringSecurity相关依赖,目的是为了 ...

  2. windbg排查大内存

    现在都是用windbg preview,安装比较麻烦了,还要配置环境变量, 并且每次分析前要先执行 !analyze - v !eeheap -gc !DumpHeap -min 500 000002 ...

  3. BFC和清除浮动

    1.清浮动(不考虑兼容的话这一项够用了): .clear:after{ content:''; display:block; clear:both; } 兼容ie6或7 加一个 .clear{ *zo ...

  4. Yarn的Linking dependencies特别慢的优化方法

    通过以下方法,可以提升部分速度: 把项目文件夹添加进杀毒软件的白名单 把Yarn的缓存文件夹(yarn cache dir查看路径)添加进杀毒软件白名单 把Yarn的安装目录添加进杀毒软件白名单 把N ...

  5. robotframework+selenium搭配chrome浏览器,web测试案例(搭建篇)

    这两天发布版本 做的事情有点多,都没有时间努力学习了,先给自己个差评,今天折腾了一天, 把robotframework 和 selenium 还有appnium 都研究了一下 ,大概有个谱,先说说we ...

  6. java反射获取Object的属性和值

    在看反射顺便做个笔记,目前知道的反射的Object都是要有对象的也就是实体Bean. import java.lang.reflect.Field; import java.util.ArrayLis ...

  7. nginx常用服务配置

    一.nginx.conf的配置方式,创建新vhost user nginx; worker_processes ; worker_cpu_affinity ; worker_rlimit_nofile ...

  8. Python Day 10

    阅读目录: 函数 函数的使用 函数的分类 函数的返回值 ##函数 ##函数的定义-----what?----什么是函数: # 函数:完成 特定 功能的代码块,作为一个整体,对其进行特定的命名,该名字就 ...

  9. MUI动态生成轮播图片

    $$.ajax({ url:'http://localhost:8080/api/v1/food/listFeatureFood', type:'Get', xhrFields: {withCrede ...

  10. shell遍历文件夹

    遍历目录下的所有文件 假如有一个文件夹路径为dir,遍历文件 for file in /path/dir/* do if test -f $file then echo $file arrary=($ ...