文档章节

如果要求精确的答案,请避免使用float和double

目翟
 目翟
发布于 2013/11/19 10:51
字数 917
阅读 944
收藏 38

        floatdouble类型的主要设计目的是为了科学计算和工程计算。它们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。因为要让一个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在二进制系统中没有精确表示。

System.out.println(1.03 – .42);
System.out.println(1.00 – 9 * .10);

输出结果为:
0.6100000000000001
0.09999999999999998

    你可能会认为,只要在打印之前将结果做一下舍入就可以解决这个问题,但遗憾的是,这种做法并不总是可行。
例如:假设你口袋里有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美元。显然这个答案是不正确的。解决这个问题的正确办法是使用BigDecimalintlong进行货币计算。

    下面看看使用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 这样就正确了 
    

关于BigDecimal的方法我们下去再仔细看看; 
    这里要说的是BigDecimal是引用数据类型

        总而言之: 对于任何要精确答案的计算任务,请不要用double和float如果你想 计算十进制小数点,并且不介意非基本类型带来的不便,那就用BigDecimal;使用 BigDecimal还有个额外的好处就是他允许你完全控制舍入,每当一个操作涉及舍入的时候 它允许你从8种舍入模式中选择一种。如果你正确通过法定要求的舍入行为进行业务计算使用BigDecimal时非常方便的;如果性能十分关键,并且你又不介意自己记录十进制的 小数点,而且涉及数值不大,就可以使用int或者long; 如果数值范围没有超过9位的十进制数字,可以用int;如果数值范围没有超过18位的十进制数字,用long 如果范围超过了18位数,那我们必须使用BigDecimal。



© 著作权归作者所有

目翟
粉丝 4
博文 25
码字总数 26183
作品 0
闸北
高级程序员
私信 提问
加载中

评论(3)

ComeFromFuture
ComeFromFuture
算账用BigDecimal,虽然性能上不是最佳,但不容易出错
fzxu_05
fzxu_05
看似有限的小数在计算机里其实是无限的
tsl0922
tsl0922
涨姿势了!
Effective Java 第三版——60. 需要精确的结果时避免使用float和double类型

Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。 60. 需要精确的结...

M104
03/20
0
0
如果需要精确的答案,请避免使用float和double

float和double类型主要是为了科学计算和工程计算而设计的。它们执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。它们并没有提供完全精确的结果。 ...

ksfzhaohui
2012/12/13
221
1
如果需要精确的答案,请避免使用float 和 double(48)

1、float 和 double 主要是为了科学计算和工程计算而设计的 为了得到广泛的数值范围内快速的精确近似值 尤其不适合货币计算 因为 float 和 double 精确的表示0.1是不可能的 BigDecimal 代替d...

职业搬砖20年
2018/07/25
5
0
《Effective Java读书笔记》--通用程序设计

将局部变量作用域最小化 最有力的技术是在第一次使用它时声明,并把它初始化。 如下代码分别用for和while对lst数组做了两次遍历。大家可以想想,for和while哪种写法更优呢? public class T...

7分04秒
2014/05/10
288
0
据说有99%的人都会做错的面试题

这道题主要考察了面试者对浮点数存储格式的理解。另外,请不要讨论该题本身是否有意义之类的话题。本题只为了测试面试者相关的知识是否掌握,题目本身并没有实际的意义。 下面有6个浮点类型变...

androidguy
2018/06/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

移动深度学习:人工智能的深水区

人工智能技术经历6年的快速发展,重新定义了很多行业的用户体验,而这仅是开始。 随着5G商用大规模落地,以及智能手机硬件性能越来越强、AIoT设备的快速普及,基于云-边缘-端算法和算力结构的...

博文视点Bv
16分钟前
2
0
vim 分屏 操作

$vim -On file file2 #大写O垂直分屏打开文件 $vim -on file file2 #小写水平打开 # n 表示分屏数,直接n等于文件个数 如果n小于文件,按顺序打开前面的n个,如果大于,打开空编辑页面 分屏快...

突突突酱
18分钟前
2
0
MySQL/Mariadb设置中文字符集(linux)

编辑/etc/my.cnf,添加以下设置 [mysql]default-character-set=utf8[mysqld]character_set_server=utf8[mysqld.safe]default-character-set=utf8[client]default-chara...

编程老陆
21分钟前
2
0
关于linux常用的挂载命令

挂载:就把一块磁盘(可以是光盘,U盘)绑定到一个空目录下面 一般情况下会挂载到mnt目录下面 挂载光盘(把光盘挂载到/mnt/cdrom这个目录中) mount -t iso9660 /dev/cdrom /mnt/cdrom 退出当前目录...

chenhongjiang
22分钟前
2
0
如何分享brain内容外部共享?几个需要知道的TheBrain问答

TheBrain(点击下载)是一款与众不同的思维导图软件,其所有信息通过一个又一个的节点进行联系,最终形成一个杂而不乱的网状结构。从头开始设计,让您捕获您的想法和信息在一个网络的联想,匹...

mnrssj
22分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部