2020-05-26 13:17:17

在Java中涉及到交易和金融货币的时候为什么不能用double、float来计算?使用BigDecimal解决


在Java中涉及到交易和金融货币的时候为什么不能用double、float来计算?使用BigDecimal解决

在Java中涉及到交易和金融货币的时候为什么不能用double、float来计算?在日常使用中double、float的浮点计算经常会出现一些意外的情况,因为二进制不能精确的表示大部分十进制的小数,今天就来讨论一下如何正确的在Java中进行小数计算。

先看案例

我们定义两个 float 变量,一个是1.0-0.9,我们预期是0.1;另一个是0.9-0.8,我们预期也是0.1,然后我们对这两个数进行打印和比较:

float a = 1.0F - 0.9F;
float b = 0.9F - 0.8F;
System.out.println("a: " + a);
System.out.println("b: " + b);
System.out.println("a == b: " + (a == b));
Float x = a;
Float y = b;
System.out.println("x.equals(y): " + x.equals(y));

运行结果是:

a: 0.100000024
b: 0.099999964
a == b: false
x.equals(y): false
java中double、float的浮点计算的精度损失

是不是超出了你的预期?在二进制的世界里小数不能很好的被表示,因为十进制小数在与二进制转换的时候会运行除法,而除法有的是除不尽的,这就造成了精度损失。

使用BigDecimal解决

java中提供了BigDecimal这个超大的类,用来存储浮点型,使用它可以处理二进制误差问题。我们使用BigDecimal改造上面的运算逻辑:

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b); // 1.0 - 0.9
BigDecimal y = b.subtract(c); // 0.9 -0.8
System.out.println("x == y: " + (x == y)); // BigDecimal 是对象所以不能直接使用 == 进行比较
System.out.println("x.equals(y): " + x.equals(y));

运行结果是:

x == y: false
x.equals(y): true

使用BigDecimal的注意点

由于精度损失问题,使用new BigDecimal()的时候,我们只能使用参数为String的构造函数,也可以使用 BigDecimal.valueOf() 的方式,因为BigDecimal.valueOf()里面调用了Double.toString()方法,根据double实际表达能力对尾数进行了截断,我们可以查看一下JDK里面的源代码:

public static BigDecimal valueOf(double val) {
    // Reminder: a zero double returns '0.0', so we cannot fastpath
    // to use the constant ZERO.  This might be important enough to
    // justify a factory approach, a cache, or a few private
    // constants, later.
    return new BigDecimal(Double.toString(val));
}

商业用途请联系作者获得授权。
版权声明:本文为博主「任霏」原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://www.renfei.net/posts/1003372
评论与留言
以下内容均由网友提交发布,版权与真实性无法查证,请自行辨别。

本站有缓存策略,时间约2小时后能看到您的评论。本站使用自动审核机制,如果您的内容包含广告/谩骂/恐怖/暴力/涉政等不和谐内容将无法展示!

111 2021-06-01 15:56:00 - Zhengzhou, Henan, China

11

回复

本站有缓存策略,时间约2小时后能看到您的评论。本站使用自动审核机制,如果您的内容包含广告/谩骂/恐怖/暴力/涉政等不和谐内容将无法展示!

关注任霏博客
扫码关注「任霏博客」微信订阅号
微博:任霏博客网
Twitter:@renfeii
Facebook:任霏
最新留言 感谢🙏,第一个问题是空格的问题应该,我逐字敲完后可以构建了.第二个问题是我docker环境的问题,docker更新为最新版后需要重置配置文件.现已经正常使用,再次感谢您的分享和您的细心解答,期待下次相遇😄 还有一个问题可以请教下吗?就是我在容器里建文件夹没有权限,su root后密码不知道是多少,sudo mkdir xxx 提示我,没有sudo命令,请问有好的解决方法吗?谢谢解答 -v 后面可以指定文件吗 我的也是报错,还有。我执行了这个:@localhost kingbase-es-v8-r3-docker % docker run -d --name kingbase -p 54321:54321 -e SYSTEM_PWD=SYSTEM -v /opt/kingbase/data:/opt/kingbase/data -v /opt/kingbase:/opt/kingbase/Server/bin kingbase:v8r3 docker: 'run -d --name kingbase -p 54321:54321 -e SYSTEM_PWD=SYSTEM -v /opt/kingbase/data:/opt/kingbase/data -v /opt/kingbase:/opt/kingbase/Server/bin kingbase:v8r3' is not a docker command. See 'docker --help' 麻烦帮忙看下,是不是我写的命令有问题,还是版本问题,谢谢啦 请问我build的时候一直报错,是资源没了吗?failed to solve with frontend dockerfile.v0: failed to create LLB definition: failed to do request: Head "https://reg-mirror.qiniu.com/v2/library/centos/manifests/7?ns=docker.io": Moved Permanently 能不能在代码那里详细解释一下啊,没完全懂呀 en 按照路径上的来操作的,但是启动时一直报:zsh: no such file or directory: docker run -d --name kingbase -p 54321:54321 -e SYSTEM_PWD=SYSTEM -v /Volumes/installation/opt/kingbase/data:/opt/kingbase/data -v /Volumes/installation/opt/kingbase/bin/license.dat:/opt/kingbase/Server/bin/license.dat kingbase:v8r3 错误 博主,怎么没有spark入门教程? 亲测可用,十分感谢。 讲得好 我想提示博主,Adblock 广告屏蔽插件被启用提示这个弹窗非常不友好。它唯一的作用是影响用户阅读,甚至厌恶了离开。没有哪个用户会为了某一个站点而关闭防广告插件,因为这对于用户是不明智也不理智的,最终的伤害是贵站。