使用 BigDecimal 进行浮点数运算
BigDecimal 介绍
BigDecimal
可以实现对浮点数的运算,不会造成精度丢失。
通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal
来做的。
《阿里巴巴 Java 开发手册》中提到:浮点数之间的等值判断,基本数据类型不能用 == 来比较,包装数据类型不能用 equals 来判断。
具体原因我们在上面已经详细介绍了,这里就不多提了。
想要解决浮点数运算精度丢失这个问题,可以直接使用 BigDecimal
来定义浮点数的值,然后再进行浮点数的运算操作即可。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);System.out.println(x.compareTo(y));// 0
BigDecimal 常见方法
创建
我们在使用 BigDecimal
时,为了防止精度丢失,推荐使用它的BigDecimal(String val)
构造方法或者 BigDecimal.valueOf(double val)
静态方法来创建对象。
《阿里巴巴 Java 开发手册》对这部分内容也有提到,如下图所示。
加减乘除
add
方法用于将两个 BigDecimal
对象相加,subtract
方法用于将两个 BigDecimal
对象相减。multiply
方法用于将两个 BigDecimal
对象相乘,divide
方法用于将两个 BigDecimal
对象相除。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.add(b));// 1.9
System.out.println(a.subtract(b));// 0.1
System.out.println(a.multiply(b));// 0.90
System.out.println(a.divide(b));// 无法除尽,抛出 ArithmeticException 异常
System.out.println(a.divide(b, 2, RoundingMode.HALF_UP));// 1.11
这里需要注意的是,在我们使用 divide
方法的时候尽量使用 3 个参数版本,并且RoundingMode
不要选择 UNNECESSARY
,否则很可能会遇到 ArithmeticException
(无法除尽出现无限循环小数的时候),其中 scale
表示要保留几位小数,roundingMode
代表保留规则。
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) {return divide(divisor, scale, roundingMode.oldMode);
}
保留规则非常多,这里列举几种:
public enum RoundingMode {// 2.5 -> 3 , 1.6 -> 2// -1.6 -> -2 , -2.5 -> -3UP(BigDecimal.ROUND_UP),// 2.5 -> 2 , 1.6 -> 1// -1.6 -> -1 , -2.5 -> -2DOWN(BigDecimal.ROUND_DOWN),// 2.5 -> 3 , 1.6 -> 2// -1.6 -> -1 , -2.5 -> -2CEILING(BigDecimal.ROUND_CEILING),// 2.5 -> 2 , 1.6 -> 1// -1.6 -> -2 , -2.5 -> -3FLOOR(BigDecimal.ROUND_FLOOR),// 2.5 -> 3 , 1.6 -> 2// -1.6 -> -2 , -2.5 -> -3HALF_UP(BigDecimal.ROUND_HALF_UP),//......
}
大小比较
a.compareTo(b)
: 返回 -1 表示 a
小于 b
,0 表示 a
等于 b
, 1 表示 a
大于 b
。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.compareTo(b));// 1
保留几位小数
通过 setScale
方法设置保留几位小数以及保留规则。保留规则有挺多种,不需要记,IDEA 会提示。
BigDecimal m = new BigDecimal("1.255433");
BigDecimal n = m.setScale(3,RoundingMode.HALF_DOWN);
System.out.println(n);// 1.255
BigDecimal 等值比较问题
《阿里巴巴 Java 开发手册》中提到:
BigDecimal
使用 equals()
方法进行等值比较出现问题的代码示例:
BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("1.0");
System.out.println(a.equals(b));//false
这是因为 equals()
方法不仅仅会比较值的大小(value)还会比较精度(scale),而 compareTo()
方法比较的时候会忽略精度。
1.0 的 scale 是 1,1 的 scale 是 0,因此 a.equals(b)
的结果是 false。
compareTo()
方法可以比较两个 BigDecimal
的值,如果相等就返回 0,如果第 1 个数比第 2 个数大则返回 1,反之返回-1。
BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("1.0");
System.out.println(a.compareTo(b));//0
总结
浮点数没有办法用二进制精确表示,因此存在精度丢失的风险。
不过,Java 提供了BigDecimal
来操作浮点数。BigDecimal
的实现利用到了 BigInteger
(用来操作大整数), 所不同的是 BigDecimal
加入了小数位的概念。
使用 BigDecimal 进行浮点数运算相关推荐
- bigdicmal除法精度设置_Java BigDecimal浮点数运算--如何保证运算精度不溢出
加减乘除四则运算是高级程序设计语言(不论机器语言.汇编还是其他高级语言)最基础的部分,Java作为最流行的软件开发语言之一,涉及四则运算的程序代码和功能业务随处可见.在笔者从事的基于Java语言银行. ...
- Java中使用BigDecimal进行浮点数精确计算 超大整数 浮点数等计算,没有数位限制...
(注:前面写了一个超大整数相加的类,参见: 超大整数相加,超过了long的范围,你要怎么做!,后来有朋友评论说BigDecimal可以完全实现我的这这个功能,刚开始的时候,我还不服气,据我所知那里有这 ...
- 使用BigDecimal进行精确运算
首先我们先来看如下代码示例: 1 public class Test_1 { 2 public static void main(String[] args) { 3 System.out.print ...
- Java中使用BigDecimal进行浮点数精确计算 超大整数 浮点数等计算 没有数位限制
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! (注:前 ...
- 关于Java浮点数运算精度丢失问题
2019独角兽企业重金招聘Python工程师标准>>> 关于Java浮点数运算精度丢失问题 博客分类: java 前几天看了一个朋友的博客,说Java中浮点数运算精度丢失的问题,他给 ...
- 详解BigDecimal及其加减乘除运算
目录 一.BigDecimal概述 二.构造函数详解 1.BigDecimal(int val) 2.BigDecimal(int val, MathContext mc) MathContext 3 ...
- 浮点数的输入以及浮点数运算
浮点数的输入以及浮点数运算 写在前面 上一次我们讲解了IEEE的标准,还记得多少? 之前我提到过,有很多小数是二进制浮点数无法表示的,因此就难免会遇到舍入的问题.这一点其实在我们平时的计算当中会经常出 ...
- java 浮点数运算_对于同样的浮点数运算为何 Java 与 C 的结果不相同?
@bombless 在问题的评论里写得没错.IEEE 754最重要的(大家基本上遵守的)是数据的格式.虽然也有算法上的指引(例如有各种rounding mode),但实际上大家实现得不一定那么严格. ...
- Java浮点数运算工具类
import java.math.BigDecimal; import java.math.RoundingMode;/*** 精确的浮点数运算*/ public class Arith {/** 默 ...
最新文章
- awk截取字符命令_Linux运维基础技能: 脚本编程与Linux命令
- NIO源码解析:IntBuffer基本使用
- Nginx域名访问与访问控制
- 钱荒下银行理财收益率角逐:邮储银行垫底
- gc.collect()==>python的强制垃圾收集机制(不建议使用强制回收,因为可能导致错误)
- 程序—java年月日转换
- Objcet_类的方法
- AcWing 904. 虫洞(SPFA or Djakarta or bellman判负环)
- vsan双主机配置_5千右预算,兼顾Pr剪辑、Ps修图、CAD制图的高性价比DIY主机配置...
- pythonint函数的参数_pythonint函数怎么用
- 谷歌大脑科学家 Caffe缔造者 贾扬清 微信讲座
- Springboot配置devtools实现热部署
- VS2012更改/重置默认开发环境
- MPU6050配置低功耗和中断
- CCNA2.0笔记_动态路由
- gitalk 未找到相关的Issues进行评论解决方法
- 您访问的网页出错了!
- Mapper代理文件实现
- 等保测评方案怎么做?按照这个流程来,轻松又省心!
- TCP/IP滑动窗口
热门文章
- 农村大学生的逆袭009
- Windows快捷键---原创总结zyh
- android手机如何拥有苹果表情包,安卓手机emoji表情怎么改成苹果的
- VC2017编译zxing
- 实践检验递归查询SQL
- traceback.print_exc()跟traceback.format_exc()有什么区别
- 神经网络和贝叶斯网络关系
- Object isExtensible()方法
- 【电子学会】2021年06月图形化二级 -- 小瓢虫找妈妈
- U盘安装win10时,出现“Windows无法打开所需文件D:\Sources\install.wim”解决办法