前言
本篇介绍下BigDecimal;
没有实质的了解过BigDecimal之前,只大概了解过它是适用于精确计算的类型;
而我们常用的float和double这两个类型主要用于科学计算和工程计算,它们执行二进制浮点运算,是为了在广域数值范围上提供较为精确的快速近似计算而设计的。所以并不适合计算精确的数值
今天来学习下适用于商业计算的BigDecimal类型。
1.我们先看下通常计算挠头的事情
public static void main(String[] args) {
System.out.println(0.1 + 0.2);
System.out.println(0.2 - 0.05);
System.out.println(0.2 * 0.1);
System.out.println(0.3 / 0.1);
}
//计算结果
0.30000000000000004
0.15000000000000002
0.020000000000000004
2.9999999999999996
// 我*你*
这结果谁看谁不头疼?但是是什么原因呢?
原因是因为的我们的计算机是二进制的,但是二进制是么有办法准确的表示浮点数的
CPU表示浮点数是由指数和尾数两部分组成的,很显然这样的表示方法会丢失一定的精度,所以有些浮点数我们计算的时候是会存在一定的误差的
2.我们看下BigDecimal
构造方法 |
---|
BigDecimal(BigInteger val) 将 BigInteger 转换成 BigDecimal 。 |
BigDecimal(BigInteger unscaledVal, int scale) 将BigInteger的 BigInteger 值和 int 等级转换为 BigDecimal 。 |
BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) 将 BigInteger 未缩放值和 int 扩展转换为 BigDecimal ,根据上下文设置进行舍入。 |
BigDecimal(BigInteger val, MathContext mc) 根据上下文设置将 BigInteger 转换为 BigDecimal 舍入。 |
BigDecimal(char[] in) 一个转换的字符数组表示 BigDecimal 成 BigDecimal ,接受字符作为的相同序列 BigDecimal(String) 构造。 |
BigDecimal(char[] in, int offset, int len) 一个转换的字符数组表示 BigDecimal 成 BigDecimal ,接受字符作为的相同序列 BigDecimal(String) 构造,同时允许一个子阵列被指定。 |
BigDecimal(char[] in, int offset, int len, MathContext mc) 一个转换的字符数组表示 BigDecimal 成 BigDecimal ,接受字符作为的相同序列 BigDecimal(String) 构造,同时允许指定一个子阵列和用根据上下文设置进行舍入。 |
BigDecimal(char[] in, MathContext mc) 一个转换的字符数组表示 BigDecimal 成 BigDecimal ,接受相同的字符序列作为 BigDecimal(String) 构造与根据上下文设置进行舍入。 |
BigDecimal(double val) 将 double 转换为 BigDecimal ,这是 double 的二进制浮点值的精确十进制表示。 |
BigDecimal(double val, MathContext mc) 将 double 转换为 BigDecimal ,根据上下文设置进行舍入。 |
BigDecimal(int val) 将 int 成 BigDecimal 。 |
BigDecimal(int val, MathContext mc) 将 int 转换为 BigDecimal ,根据上下文设置进行舍入。 |
BigDecimal(long val) 将 long 成 BigDecimal 。 |
BigDecimal(long val, MathContext mc) 将 long 转换为 BigDecimal ,根据上下文设置进行舍入。 |
BigDecimal(String val) 将BigDecimal的字符串表示 BigDecimal 转换为 BigDecimal 。 |
BigDecimal(String val, MathContext mc) 一个转换的字符串表示 BigDecimal 成 BigDecimal ,接受相同的字符串作为 BigDecimal(String) 构造,利用根据上下文设置进行舍入。 |
找出4个最常用的方法
1.BigDecimal(int) 创建一个具有参数所指定整数值的对象。
2.BigDecimal(double) 创建一个具有参数所指定双精度值的对象。(不建议采用)
3.BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象 。
4.BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
public static void main(String[] args) {
int a = 2;
double b = 2.3d;
long c = 2L;
String d = "2.3";
BigDecimal bigDecimal = new BigDecimal(a);
BigDecimal bDouble = new BigDecimal(b);
BigDecimal bLong = new BigDecimal(c);
BigDecimal bString = new BigDecimal(d);
System.out.println("bigDecimal=" + bigDecimal);
System.out.println("bDouble=" + bDouble);
System.out.println("bLong=" + bLong);
System.out.println("bString=" + bString);
}
//结果
bigDecimal=2
bDouble=2.29999999999999982236431605997495353221893310546875
bLong=2
bString=2.3
这是因为double的构造方法有一定的不可预知性。
建议是使用[
BigDecimal(String val)
将BigDecimal的字符串表示BigDecimal
转换为BigDecimal
]String的构造方法是完全可以预知的
如果非得使用double的时候,我们可以将double转为String后在使用String的构造函数Double.toString(double)
3.BigDecimal的方法
类型 | 说明 |
---|---|
BigDecimal |
abs() 返回一个 BigDecimal ,其值为此 BigDecimal 的绝对值,其缩放比例为 this.scale() 。 |
BigDecimal |
abs(MathContext mc) 返回一个 BigDecimal ,其值为此 BigDecimal 的绝对值,根据上下文设置进行舍入。 |
BigDecimal |
add(BigDecimal augend) 返回 BigDecimal ,其值是 (this + augend) ,其标为 max(this.scale(), augend.scale()) 。 |
BigDecimal |
add(BigDecimal augend, MathContext mc) 返回 BigDecimal ,其值是 (this + augend) ,根据上下文设置进行舍入。 |
byte |
byteValueExact() 将此 BigDecimal 转换为 byte ,检查丢失的信息。 |
int |
compareTo(BigDecimal val) 将此 BigDecimal 与指定的BigDecimal进行 BigDecimal 。 |
BigDecimal |
divide(BigDecimal divisor) 返回BigDecimal ,其值为(this / divisor) ,优先级为(this.scale() - divisor.scale()) ; 如果不能表示确切的商(因为它具有非终止的十进制扩展),则抛出一个ArithmeticException 。 |
BigDecimal |
divide(BigDecimal divisor, int roundingMode) 返回 BigDecimal ,其值是 (this / divisor) ,其标为 this.scale() 。 |
BigDecimal |
divide(BigDecimal divisor, int scale, int roundingMode) 返回一个 BigDecimal ,其值为 (this / divisor) ,其比例为指定。 |
BigDecimal |
divide(BigDecimal divisor, int scale, RoundingMode roundingMode) 返回一个 BigDecimal ,其值为 (this / divisor) ,其比例为指定。 |
BigDecimal |
divide(BigDecimal divisor, MathContext mc) 返回 BigDecimal ,其值是 (this / divisor) ,根据上下文设置进行舍入。 |
BigDecimal |
divide(BigDecimal divisor, RoundingMode roundingMode) 返回 BigDecimal ,其值是 (this / divisor) ,其标为 this.scale() 。 |
BigDecimal[] |
divideAndRemainder(BigDecimal divisor) 返回一个两元件 BigDecimal 阵列含有的结果 divideToIntegralValue 接着的结果 remainder 上的两个操作数。 |
BigDecimal[] |
divideAndRemainder(BigDecimal divisor, MathContext mc) 返回一个两元件 BigDecimal 阵列含有的结果 divideToIntegralValue 接着的结果 remainder 上与根据上下文设置进行舍入计算出的两个操作数。 |
BigDecimal |
divideToIntegralValue(BigDecimal divisor) 返回一个 BigDecimal ,它的值是 BigDecimal 的整数部分 (this / divisor) 取整。 |
BigDecimal |
divideToIntegralValue(BigDecimal divisor, MathContext mc) 返回值为 BigDecimal 的整数部分的 (this / divisor) 。 |
double |
doubleValue() 将此 BigDecimal 转换为 double 。 |
boolean |
equals(Object x) 将此 BigDecimal 与指定的 Object 进行比较以获得相等性。 |
float |
floatValue() 将此 BigDecimal 转换为 float 。 |
int |
hashCode() 返回此 BigDecimal 的哈希码。 |
int |
intValue() 将此 BigDecimal 转换为 int 。 |
int |
intValueExact() 将此 BigDecimal 转换为 int ,检查丢失的信息。 |
long |
longValue() 将此 BigDecimal 转换为 long 。 |
long |
longValueExact() 将此 BigDecimal 转换为 long ,检查丢失的信息。 |
BigDecimal |
max(BigDecimal val) 返回此 BigDecimal 和 val 。 |
BigDecimal |
min(BigDecimal val) 返回此 BigDecimal 和 val 。 |
BigDecimal |
movePointLeft(int n) 返回一个 BigDecimal ,相当于这个小数点,向左移动了 n 个地方。 |
BigDecimal |
movePointRight(int n) 返回一个 BigDecimal ,相当于这个小数点移动了 n 个地方。 |
BigDecimal |
multiply(BigDecimal multiplicand) 返回 BigDecimal ,其值是 (this × multiplicand), 其标为 (this.scale() + multiplicand.scale()) 。 |
BigDecimal |
multiply(BigDecimal multiplicand, MathContext mc) 返回 BigDecimal ,其值是 (this × multiplicand), 根据上下文设置进行舍入。 |
BigDecimal |
negate() 返回 BigDecimal ,其值是 (-this) ,其标为 this.scale() 。 |
BigDecimal |
negate(MathContext mc) 返回 BigDecimal ,其值是 (-this) ,根据上下文设置进行舍入。 |
BigDecimal |
plus() 返回 BigDecimal ,其值是 (+this) ,其标为 this.scale() 。 |
BigDecimal |
plus(MathContext mc) 返回 BigDecimal ,其值是 (+this) ,根据上下文设置进行舍入。 |
BigDecimal |
pow(int n) 返回 BigDecimal ,其值是 (thisn), 该电源,准确计算,使其具有无限精度。 |
BigDecimal |
pow(int n, MathContext mc) 返回 BigDecimal ,其值是 (thisn)。 |
int |
precision() 返回此 BigDecimal的 BigDecimal 。 |
BigDecimal |
remainder(BigDecimal divisor) 返回 BigDecimal ,其值是 (this % divisor) 。 |
BigDecimal |
remainder(BigDecimal divisor, MathContext mc) 返回 BigDecimal ,其值是 (this % divisor) ,根据上下文设置进行舍入。 |
BigDecimal |
round(MathContext mc) 返回 BigDecimal 根据四舍五入 MathContext 设置。 |
int |
scale() 返回此 规模 BigDecimal 。 |
BigDecimal |
scaleByPowerOfTen(int n) 返回一个BigDecimal,其数值等于( this * 10 n )。 |
BigDecimal |
setScale(int newScale) 返回一个 BigDecimal ,其大小是指定值,其值在数字上等于此 BigDecimal 。 |
BigDecimal |
setScale(int newScale, int roundingMode) 返回一个 BigDecimal ,其规模是指定值,其缩放值通过将此 BigDecimal 的非标度值乘以10的适当功率来确定,以维持其总体值。 |
BigDecimal |
setScale(int newScale, RoundingMode roundingMode) 返回一个 BigDecimal ,其规模是指定值,其缩放值通过将该 BigDecimal 的非标度值乘以10的适当功率来确定,以维持其整体值。 |
short |
shortValueExact() 将此 BigDecimal 转换为 short ,检查丢失的信息。 |
int |
signum() 返回这个 BigDecimal 的signum函数。 |
BigDecimal |
stripTrailingZeros() 返回一个 BigDecimal ,它在数字上等于此值, BigDecimal 表示中删除任何尾随的零。 |
BigDecimal |
subtract(BigDecimal subtrahend) 返回 BigDecimal ,其值是 (this - subtrahend) ,其标为 max(this.scale(), subtrahend.scale()) 。 |
BigDecimal |
subtract(BigDecimal subtrahend, MathContext mc) 返回 BigDecimal ,其值是 (this - subtrahend) ,根据上下文设置进行舍入。 |
BigInteger |
toBigInteger() 将此 BigDecimal 转换为 BigInteger 。 |
BigInteger |
toBigIntegerExact() 将此 BigDecimal 转换为 BigInteger ,检查丢失的信息。 |
String |
toEngineeringString() 如果需要指数,则使用工程符号返回此 BigDecimal 的字符串表示形式。 |
String |
toPlainString() 返回没有指数字段的此 BigDecimal 的字符串表示形式。 |
String |
toString() 返回此 BigDecimal 的字符串表示,如果需要指数,则使用科学计数法。 |
BigDecimal |
ulp() 返回此 BigDecimal 的最后一个位置的ulp(一个单位)的大小。 |
BigInteger |
unscaledValue() 返回一个 BigInteger ,其值是此 BigDecimal 的 未缩放值 。 |
static BigDecimal |
valueOf(double val) 转换一个 double 成 BigDecimal ,使用 double 通过所提供的规范的字符串表示 Double.toString(double) 方法。 |
static BigDecimal |
valueOf(long val) 将 long 值转换为 BigDecimal ,比例为零。 |
static BigDecimal |
valueOf(long unscaledVal, int scale) 将 long 值和 int 比例转换为 BigDecimal 。 |
有点太多了 60个方法
简单列出我们常用的几个:
add(BigDecimal) //BigDecimal对象中的值相加,然后返回这个对象。
subtract(BigDecimal) //BigDecimal对象中的值相减,然后返回这个对象。
multiply(BigDecimal) //BigDecimal对象中的值相乘,然后返回这个对象。
divide(BigDecimal) //BigDecimal对象中的值相除,然后返回这个对象。
toString() //将BigDecimal对象的数值转换成字符串。
doubleValue() //将BigDecimal对象中的值以双精度数返回。
floatValue() //将BigDecimal对象中的值以单精度数返回。
longValue() //将BigDecimal对象中的值以长整数返回。
intValue() //将BigDecimal对象中的值以整数返回。
简单的加减乘除方法;
需要单拿出来说一下的是divide(BigDecimal)
; 当不能整除的时候就会报错!
public static void main(String[] args) {
BigDecimal a=new BigDecimal(6.5);
BigDecimal b=new BigDecimal(1.2);
System.out.println("a / b =" + a.divide(b));
}
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1690)
at com.sqzy.softplat.malls.service.impl.OrderReturnServiceImpl.main(OrderReturnServiceImpl.java:226)
解决方法:
BigDecimal
divide(BigDecimal divisor, int scale, int roundingMode)
返回一个 BigDecimal
,其值为 (this / divisor)
,其比例为指定。
我们看下这个入参;
第一参数表示除数, 第二个参数表示小数点后保留位数,第三个参数表示舍入模式
也只有在除法或四舍五入的时候才用到舍入模式
-
ROUND_UP
public static final int ROUND_UP
舍入模式从零开始。 始终在非零丢弃分数之前增加数字。 请注意,该舍入模式不会降低计算值的大小。
-
另请参见:
-
ROUND_DOWN
public static final int ROUND_DOWN
舍入模式向零舍入。 不要在丢弃的分数之前递增数字(即截断)。 请注意,该舍入模式不会增加计算值的大小。
-
另请参见:
-
-
ROUND_CEILING
public static final int ROUND_CEILING
圆形模式向正无穷大转弯。 如果
BigDecimal
为正,则表现为ROUND_UP
; 如果为负,则表现为ROUND_DOWN
。 请注意,舍入模式不会降低计算值。-
另请参见:
-
-
ROUND_FLOOR
public static final int ROUND_FLOOR
舍入模式向负无穷大转弯。 如果
BigDecimal
为正,则表现为ROUND_DOWN
; 如果为负,表现为ROUND_UP
。 请注意,舍入模式不会增加计算值。-
另请参见:
-
-
ROUND_HALF_UP
public static final int ROUND_HALF_UP
四舍五入模式向“最近邻居”转弯,除非两个邻居都是等距的,在这种情况下是圆括弧的。 对于
ROUND_UP
如果丢弃的分数为0.5,则表现为; 否则,表现为ROUND_DOWN
。 请注意,这是我们大多数人在小学教学的舍入模式。-
另请参见:
-
-
ROUND_HALF_DOWN
public static final int ROUND_HALF_DOWN
四舍五入模式向“最近邻居”转弯,除非这两个邻居都是等距离的,在这种情况下,这是倒圆的。 对于
ROUND_UP
如果丢弃的分数> 0.5,则表示行为; 否则,表现为ROUND_DOWN
。-
另请参见:
-
-
ROUND_HALF_EVEN
public static final int ROUND_HALF_EVEN
四舍五入模式向“最近邻居”转弯,除非两个邻居都是等距离的,在这种情况下,向着邻居方向转移。 对于
ROUND_HALF_UP
行为,如果丢弃的分数的左边的数字是奇数的; 像ROUND_HALF_DOWN
一样,如果它是均匀的。 请注意,这是在一系列计算中重复应用时最小化累积误差的舍入模式。-
另请参见:
-
-
ROUND_UNNECESSARY
public static final int ROUND_UNNECESSARY
舍入模式来确定所请求的操作具有精确的结果,因此不需要舍入。 如果在产生不精确结果的操作上指定了舍入模式,则抛出
ArithmeticException
。-
另请参见:
-
根据自己的需求来传入参数:
public static void main(String[] args) {
BigDecimal a=new BigDecimal(6.5);
BigDecimal b=new BigDecimal(1.2);
System.out.println("a / b =" + a.divide(b,2, RoundingMode.HALF_UP));
}
//结果 5.41666667
a / b =5.42
另外还有个小方法:比较BigDecimal的大小
-
compareTo
public int compareTo(BigDecimal val)
将此
BigDecimal
与指定的BigDecimal进行BigDecimal
。 两个BigDecimal
对象的价值相等但具有不同的比例(如2.0和2.00)被认为是相等的这种方法。 该方法优先于六个布尔比较运算符(<,==,>,> =,!=,<=)中的每一个的各个方法。 用于执行这些比较的建议成语是:(x.compareTo(y)
< op >0)
,其中< op >是六个比较运算符之一。-
Specified by:
compareTo
中的Comparable<BigDecimal>
-
参数
val
-BigDecimal
,对此BigDecimal
进行比较。 -
结果
-1,0或1,因为这个
BigDecimal
在数字上小于,等于或大于val
。 -
equals
public boolean equals(Object x)
将此
BigDecimal
与指定的Object
进行比较以获得相等性。 与compareTo
不同,该方法认为两个BigDecimal
对象只有在值和比例相等时才相等(因此,当通过此方法比较时,2.0不等于2.00)。-
重写:
equals
在Object
-
参数
x
-Object
要对这BigDecimal
进行比较。 -
结果
true
当且仅当指定的Object
为BigDecimal
,其值和比例等于该BigDecimal
的时候。 -
另请参见:
-
-
4.格式化
既然是专业做商业计算的BigDecimal怎么能没有专业的商业计算的风格
接下来我们小小的计算下某付宝的七日年化
public static void main(String[] args) {
//货币格式化
NumberFormat currency = NumberFormat.getCurrencyInstance();
//百分比格式化
NumberFormat percent = NumberFormat.getPercentInstance();
//百分比小数点
percent.setMaximumFractionDigits(3);
//本金
BigDecimal loanAmount = new BigDecimal("8000.00");
//七日年化率 %
BigDecimal interestRate = new BigDecimal("0.0164");
//公式 10000*2.3%÷365≈0.63元
BigDecimal interest = loanAmount.multiply(interestRate).divide(new BigDecimal(365),2,RoundingMode.UP);
System.out.println("七日年化:\t" + percent.format(interestRate));
System.out.println("贷款金额:\t" + currency.format(loanAmount));
System.out.println("每日:\t" + currency.format(interest));
}
//计算结果 8000块在七日年化率为1.64%的情况下,每天的收益大概是0.36左右
七日年化: 1.64%
本金: ¥8,000.00
每日: ¥0.36