ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【Java】BigDecimal类的使用

2021-12-30 22:36:26  阅读:207  来源: 互联网

标签:舍入 Java BigDecimal UP b1 b2 使用 ROUND


BigDecimal 由任意精度的整数非标度值和32位的整数标度(scale)组成。BigDecimal表示的数值是(unscaledValue × 10-scale)

BigDecimal 的存在原因

任何十进制整数都可以精确转换成一个二进制整数,但任何一个十进制小数却不一定能精确转换为一个二进制小数。例如 0.7转为二进制是 0.1011001100110011...

用有限的二进制位无法准确表示某些小数,当我们在计算时就会损失精度。

Java 里用 float 和 double 来进行某些小数之间的计算可能会损失精度。例如

double d1 = 2.9;
double d2 = 2.5;
System.out.println(d1-d2);
//结果是0.3999999999999999

这在商业计算领域无疑是不被允许的,怎么解决这个问题呢?

BigDecimal 就是用来进行这种精确计算的。

BigDecimal 的实现原理

任何十进制整数都可以精确转换为有限位的二进制整数。

通过把小数扩大到整数的维度上进行计算即可保留相应的精度信息。

下面来看下 BigDecimal 类的部分源码。

image-20211230202421010

  • intVal 字段是存放整数非标度值(放大了的整数)。当该 BigDecimal 的有效数的绝对值小于或等于Long.MAX_VALUE ,此字段为空。
  • scale 字段是标度(值的小数点后的位数)
  • precision 字段是此 BigDecimal 中的十进制位数(即整数非标度值的数字位数),如果位数未知(后备信息),则为 0。 如果非零,则保证该值正确。
  • intCompact 字段也是用来存整数的非标度值。如果该 BigDecimal 的有效数的绝对值小于或等于Long.MAX_VALUE ,则该值可以紧凑地存储在该字段中并用于计算。

image-20211230205015353

把 2.9 的放大成 29 作为非标度值,小数点后只有一位,故1 作为标度,这就是 BigDecimal。

BigDecimal 的构造

常用的两个构造器是

  • BigDecimal(String)
  • BigDecimal(double)

例如

BigDecimal b1 = new BigDecimal("2.9");
BigDecimal b2 = new BigDecimal(2.9);

更推荐使用通过字符串去构造 BigDecimal 。看以下运行结果

image-20211230210029702

参数类型为 double 的构造方法的结果有一定的不可预知性。利用 double 作为参数的构造函数,无法精确构造一个 BigDecimal 对象 ,而利用 String 对象作为参数传入的构造函数能精确的构造出一个 BigDecimal 对象。

也可以通过以下方法构建

BigDeciamal b3 = BigDecimal.valueOf(2.9);

image-20211230210739983

从其源码我们可以看出它本质是用参数类型为 String 的构造器创建 BigDecimal。

BigDecimal 的运算

BigDecimal 是不可变(immutable)的,它的所有操作都会生成一个新的对象

BigDecimal b1 = new BigDecimal("2.9");
BigDecimal b2 = BigDecimal.valueOf(2.5);
        
//加法 b1+b2
b1.add(b2);
//减法 b1-b2
b1.subtract(b2);
//乘法 b1*b2
b1.multiply(b2);
//除法 b1/b2
b1.divide(b2);

【注意】除法运算时,结果不能整除时会报 java.lang.ArithmeticException;解决方法如下

//divide(BigDecimal divisor, int scale, int roundingMode);
//第二个参数为保留多少位小数,第三个参数为舍入模式
b1.divide(b2,10,BigDecimal.ROUND_HALF_UP)
舍入模式 解释
ROUND_UP 向远离零的方向舍入。舍弃非零部分,并将非零舍弃部分相邻的一位数字加一。
ROUND_DOWN 向零方向舍入。舍弃非零部分,同时不会将非零舍弃部分相邻的一位数字加一,采取截取行为
ROUND_CEILING 向正无穷方向舍入。如果为正数,舍入结果同ROUND_UP一致;如果为负数,舍入结果同ROUND_DOWN一致。注意:此模式不会减少数值大小。
ROUND_FLOOR 向负无穷方向舍入。如果为正数,舍入结果同ROUND_DOWN一致;如果为负数,舍入结果同ROUND_UP一致。注意:此模式不会增加数值大小。
ROUND_HALF_UP 向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。如果舍弃部分>= 0.5,则舍入行为与ROUND_UP相同;否则舍入行为与ROUND_DOWN相同。也即“四舍五入”。
ROUND_HALF_DOWN 向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则为向下舍入的舍入模式。如果舍弃部分> 0.5,则舍入行为与ROUND_UP相同;否则舍入行为与ROUND_DOWN相同。也即“五舍六入”。
ROUND_HALF_EVEN 向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则相邻的偶数舍入。如果舍弃部分左边的数字奇数,则舍入行为与 ROUND_HALF_UP 相同;如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。也称为“银行家舍入法”。
ROUND_UNNECESSARY 不需要舍入模式

BigDecimal 的比较

不要用equals方法来比较 BigDecimal 对象,因为它的 equals 方法会比较 scale,如果 scale 不一样,它会返回 false。

/*
 b1大于b2返回1
 b1等于b2返回0
 b1小于b2返回-1
*/
int result = b1.compareTo(b2);

其他常用方法

判断正负

/*
 b1为正数返回1
 b1为零返回0
 b1为数返回-1
*/
b1.signum();

对 BigDecimal 进行截断

//第一个参数是保留的小数点后位数,第二个参数为舍入模式
b1.setScale(3, RoundingMode.HALF_UP);

标签:舍入,Java,BigDecimal,UP,b1,b2,使用,ROUND
来源: https://www.cnblogs.com/hzyuan/p/15750821.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有