[十七]基础类型BigDecimal简介详解编程语言

 

BigDecimal是不可变的、任意精度的、有符号的、十进制数.

image_5bdbee29_1474
 

组成部分

BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度 (scale) 组成
BigDecimal 表示的数值是  :
unscaledValue × 10的-scale 次幂
私有成员intVal就是非标度值
scale就是标度

image_5bdbee29_6a7

 

标度

BigDecimal由非标度值 和 32 位的整数标度 (scale) 组成
BigDecimal表示的数为: unscaledValue × 10的-scale 次幂
显然
如果scale为零或正数,最终的结果中,小数点后面的位数就等于scale标度
比如: scale为1 10的-1次方,  0.1 小数点后有1位
如果 scale 是负数,那最终的结果将会是乘以 10的|scale| 次方
比如:  scale为-3 最终的值就是非标度值乘以 1000  (    10的(- -3)次方   )
 

精度

非标度值的数字个数
 

构造方法

几个关键概念  非标度值  标度 运算规则
构造方法就是围绕这几个点展开的
BigDecimal(BigInteger val) 将 BigInteger 转换为 BigDecimal
BigDecimal(BigInteger unscaledVal,int scale)
将 BigInteger 非标度值和 int 标度转换为 BigDecimal
BigDecimal(BigInteger unscaledVal,
                   int scale,
                   MathContext mc)
将 BigInteger 非标度值和 int 标度转换为 BigDecimal
(根据上下文设置进行舍入)
BigDecimal(BigInteger val,MathContext mc)
将 BigInteger 转换为 BigDecimal(根据上下文设置进行舍入)
 
BigDecimal(char[] in, int offset, int len, MathContext mc) 将 BigDecimal 的字符数组表示形式转换为 BigDecimal
允许指定子数组
根据上下文设置进行舍入
BigDecimal(char[] in, int offset, int len) 上一个方法的简化默认形式
image_5bdbee29_24ba
BigDecimal(char[] in) 简化形式
image_5bdbee29_7fe7
BigDecimal(char[] in, MathContext mc) 简化形式
image_5bdbee29_53df
BigDecimal(String val)
调用的BigDecimal(char[] in, int offset, int len)
image_5bdbee29_566a
BigDecimal(String val, MathContext mc) 调用的是BigDecimal(char[] in, int offset, int len, MathContext mc)
image_5bdbee29_6a0
 
 
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(double val) double 转换为 BigDecimal
BigDecimal(double val, MathContext mc) double 转换为 BigDecimal
根据上下文设置进行舍入
 

构造方法注意事项

BigDecimal(double val)
BigDecimal(double val, MathContext mc)
这两个构造方法具有一定的不确定性
如下图所示,这是因为在二进制中无法准确地表示0.1 如同十进制无法准确表示 1/3 一样
image_5bdbee29_74ea
 
当 double 必须用作 BigDecimal 的源时
请注意,此构造方法public BigDecimal(double val)提供了一个准确转换;
不等同于下面的操作:
先使用 Double.toString(double) 方法,
然后使用 BigDecimal(String) 构造方法
要获取该结果,请使用 static valueOf(double) 方法
 

String构造方法的格式

Sign(可选) Significand Exponent opt(可选)
Sign 符号:
+
 
Significand 有效数字至少要有整数或者小数的一位数字:
IntegerPart .FractionPart  整数和小数
. FractionPart   小数
IntegerPart      整数
 
IntegerPart:
Digits
 
FractionPart:
Digits
 
Exponent:  指数部分
ExponentIndicator SignedInteger
 
ExponentIndicator: 指数符号
e
E
 
SignedInteger: 有符号数
Sign(可选的) Digits
 
Digits:
Digit
Digits Digit
 
Digit:
Character.isDigit(char) 对其返回 true 的任何字符,如 0、1、2……
 
-1.23E-12
这是一个完整的格式
含有符号  / 含有整数部分 / 含有小数部分 /含有指数部分/指数部分含有符号
 
除非有必要
否则在你需要 将 float 或 double 转换为 BigDecimal时
首选BigDecimal(String val)
构造方法与 Float.toString(float) 和 Double.toString(double) 返回的值兼容
它不会遇到 BigDecimal(double) 构造方法的不可预知问题
 

常量

内部定义了几个public final static int的常量,用于标注舍入模式
与RoundingMode中是一一对应的,这几个不要再使用了
请使用RoundingMode中的枚举值
ROUND_UP
ROUND_DOWN
 
ROUND_CEILING
ROUND_FLOOR
 
ROUND_HALF_UP
ROUND_HALF_DOWN
ROUND_HALF_EVEN
 
ROUND_UNNECESSARY
 

另外还有三个常用对象
public static final BigDecimal ZERO
public static final BigDecimal ONE
public static final BigDecimal TEN
 

常用方法

属性获取

int signum() 返回此 BigDecimal 的正负号函数
负、零或正时,返回 -1、0 或 1
int scale() 返回此 BigDecimal 的标度
int precision()
返回此 BigDecimal 的精度。(精度是非标度值的数字个数。)
零值的精度是 1
BigInteger unscaledValue() 返回其值为此 BigDecimal 的非标度值 的 BigInteger
image_5bdbee29_79fb
 

四则运算

除非结果准确,每种运算都有一个表示结果的首选标度
这些标度是返回准确算术结果的方法使用的标度
image_5bdbee29_54ed
 
add(BigDecimal augend)
计算 this + augend
标度为:
max(this.scale(), augend.scale())
add(BigDecimal augend, MathContext mc)
计算 this + augend
根据上下文设置进行舍入
subtract(BigDecimal subtrahend)
计算 this – subtrahend
标度为 :
max(this.scale(), subtrahend.scale())
subtract(BigDecimal subtrahend, MathContext mc)
计算 this – subtrahend
根据上下文设置进行舍入
multiply(BigDecimal multiplicand)
计算 this × multiplicand
标度为 :
(this.scale() + multiplicand.scale())
multiply(BigDecimal multiplicand, MathContext mc)
计算 this × multiplicand)
根据上下文设置进行舍入
divide(BigDecimal divisor, int scale, int roundingMode)
计算 this / divisor
指定标度
如果需要舍入则会使用指定的模式进行舍入
 
应该使用
divide(BigDecimal, int, RoundingMode)
进行替代
divide(BigDecimal divisor,
          int scale,
          RoundingMode roundingMode)
image_5bdbee29_37d5
作为上面divide方法的替代
目前仍旧映射到原来的遗留方法上
将RoundingMode转换为了int
相对于上一个方法,应该优先使用这个方法
divide(BigDecimal divisor, int roundingMode) 简化转换形式
image_5bdbee29_45c4
divide(BigDecimal divisor, RoundingMode roundingMode) 简化转换形式
image_5bdbee29_768a
divide(BigDecimal divisor) 计算 this / divisor
首选标度为 (this.scale() – divisor.scale());
如果无法表示准确的商值(因为它有无穷的十进制扩展)
则抛出 ArithmeticException
divide(BigDecimal divisor, MathContext mc) 计算 this / divisor
根据上下文设置进行舍入
 
divideToIntegralValue(BigDecimal divisor) 返回 BigDecimal
值为向下舍入所得商值 (this / divisor) 的整数部分
首选标度为 (this.scale() – divisor.scale())
divideToIntegralValue(BigDecimal divisor, MathContext mc) 返回 BigDecimal
其值为 (this / divisor) 的整数部分
准确商值的整数部分与舍入模式无关
所以舍入模式不影响此方法返回的值
首选标度是 (this.scale() – divisor.scale())

如果准确商值的整数部分需要的位数多于 mc.precision
则抛出 ArithmeticException

 
divideToIntegralValue 需要注意因为是取整数部分,所以舍入模式是不影响的
针对于参数MathContext 有影响的是精度
 
BigDecimal[] divideAndRemainder(BigDecimal divisor)  计算商和余数
返回由两个元素组成的 BigDecimal 数组
该数组包含 divideToIntegralValue 的结果
后跟对两个操作数计算所得到的 remainder

BigDecimal[] divideAndRemainder(BigDecimal divisor, MathContext mc)
计算商和余数
返回由两个元素组成的 BigDecimal 数组
该数组包含 divideToIntegralValue 的结果
后跟
根据上下文设置对两个操作数进行舍入计算所得到的 remainder
 
remainder(BigDecimal divisor) image_5bdbee29_755
remainder(BigDecimal divisor, MathContext mc) image_5bdbee29_810
 

注意

如果同时需要整数商和余数
则divideAndRemainder比分别使用 divideToIntegralValue 和 remainder 方法更快速,因为相除仅需执行一次
remainder则是依赖于divideAndRemainder ,然后返回的第二个元素
 

数学方法

BigDecimal pow(int n)
求n次幂
准确计算该幂,使其具有无限精度
参数 n 必须在 0 到 999999999(包括)之间
ZERO.pow(0) 返回 ONE
-如果 n 超出范围 抛出异常ArithmeticException
pow(int, MathContext)
求n次幂
使用的是 ANSI 标准 X3.274-1996 中定义的核心算法(根据上下文设置进行舍入)
BigDecimal abs()
求绝对值
其标度为 this.scale() 
BigDecimal abs(MathContext mc)
求绝对值
根据上下文设置进行舍入
最大值max
最小值min

借助于compareTo
image_5bdbee29_573c
int compareTo(BigDecimal val)
值相等但具有不同标度的两个 BigDecimal 对象(如,2.0 和 2.00)被认为是相等的
注意:与equals中的相等含义不同
小于、等于或大于 val 时,返回 -1、0 或 1

 

equals

判断是否相等
与 compareTo 不同
仅当两个 BigDecimal 对象的值和标度都相等时,此方法才认为它们相等
(因此通过此方法进行比较时,2.0 不等于 2.00)
一定要注意到compareTo方法与equals方法 对于相等的定义是不一致的
 

valueOf

public static BigDecimal valueOf(long val)
将 long 值转换为具有零标度的 BigDecimal
这个方法优先于以long为参数的构造方法

如下图所示,这个valueOf方法会进行缓存
image_5bdbee29_758c
public static BigDecimal valueOf(long unscaledVal, int scale)
将 long 非标度值和 int 标度转换为 BigDecimal
看得出来这个valueOf版本也是会借助于缓存的
所以优先于构造方法
image_5bdbee2a_3754
valueOf(double val)
使用 Double.toString(double) 方法转换 double 为字符串
并且 调用构造方法

此方法并没有涉及到缓存
回头看下上面说的String参数类型的构造方法
String参数类型的构造方法—与 Float.toString(float) 和 Double.toString(double) 返回的值兼容
这个valueOf借助的就是toString方法
这个版本valueOf是float和double转换为BigDecimal的首选
image_5bdbee2a_1b40
 

setScale

setScale 系列并不是设置BigDecimal的scale  BigDecimal是不可变得
setScale 是一个转换器,将参数的BigDecimal转换为指定标度的值
值本身不会变化,变化的是形式
返回的是一个新的BigDecimal,不过这个新的BigDecimal并不一定是新创建的
可能是使用缓存,新是相对于调用者来说
image_5bdbee2a_355f
 
方法列表:
public BigDecimal setScale(int newScale, int roundingMode)
返回一个 BigDecimal
其标度为指定值
其非标度值通过此 BigDecimal 的非标度值乘以或除以十的适当次幂来确定,以维护其总值
相对于此遗留方法,应优先使用新的 setScale(int, RoundingMode) 方法
public BigDecimal setScale(int newScale, RoundingMode roundingMode)
setScale(int newScale, int roundingMode) 的替代形式
使用RoundingMode枚举
image_5bdbee2a_52d6
BigDecimal setScale(int newScale)
返回一个 BigDecimal
其标度为指定值,其值在数值上等于此 BigDecimal 的值
如果这不可能,则抛出 ArithmeticException

省略了模式,其实就是默认了模式,默认为 UNNECESSARY
UNNECESSARY 用于断言,所以如果结果需要舍入的话,,则会抛出异常
image_5bdbee2a_63b5
 

negate/plus/round

BigDecimal negate()
取负数
返回 BigDecimal,值为 (-this),标度为 this.scale() 
BigDecimal negate(MathContext mc)
根据指定上下文设置取负数
返回其值为 (-this) 的 BigDecimal(根据上下文设置进行舍入)。
image_5bdbee2a_7058
BigDecimal plus()
返回本身  任何一个数前面加正号 都是它本身
值为 (+this),标度为 this.scale()
image_5bdbee2a_729b
BigDecimal plus(MathContext mc)
返回其值为 (+this) 的 BigDecimal
(根据上下文设置进行舍入)
方法的效果与 round(MathContext) 方法的效果相同
BigDecimal round(MathContext mc)
等同于BigDecimal plus(MathContext mc)
image_5bdbee2a_17a
 

xxxValue

intValue()
转换为 int
丢弃此 BigDecimal 的小数部分
如果生成的 “BigInteger” 太大而不适合用 int 表示,则仅返回 32 位低位字节
此转换会丢失关于此 BigDecimal 值的总大小和精度的信息
longValue()
转换为 long
丢弃此 BigDecimal 的小数部分
如果生成的 “BigInteger” 太大
仅返回 64 位低位字节
此转换会丢失关于此 BigDecimal 值的总大小和精度的信息
floatValue()
转换为 float
如果BigDecimal 的值太大而不能表示为 float
将其适当地转换为 Float.NEGATIVE_INFINITY 或 Float.POSITIVE_INFINITY
此转换也可能丢失关于 BigDecimal 值精度的信息
doubleValue()
转换为 double
如果此 BigDecimal 的数量太大而不能表示为 double
将其适当地转换为 Double.NEGATIVE_INFINITY 或 Double.POSITIVE_INFINITY
转换也可能丢失关于 BigDecimal 值精度的信息
BigInteger toBigInteger()
转换为 BigInteger
丢弃此 BigDecimal 的小数部分
此转换会丢失关于 BigDecimal 值的精度信息
 

XXXValueExact

byte byteValueExact()
转换为 byte
如果此 BigDecimal 具有非零小数部分,或者超出 byte 结果的可能范围
抛出 ArithmeticException
image_5bdbee2a_295a
short shortValueExact()
转换为 short
如果此 BigDecimal 具有非零小数部分,或者超出 short 结果的可能范围
抛出 ArithmeticException
image_5bdbee2a_33f5
int intValueExact()
转换为 int
如果此 BigDecimal 具有非零小数部分,或者超出 int 结果的可能范围
抛出 ArithmeticException
image_5bdbee2a_4e60
long longValueExact()
转换为 long
如果此 BigDecimal 具有非零小数部分,或者超出 long 结果的可能范围
抛出 ArithmeticException
BigInteger toBigIntegerExact()
转换为 BigInteger
如果此 BigDecimal 具有非零小数部分,则抛出一个异常
 
exact版本的区别就在于是否能够准确转换,否则抛出异常
也就是他要么返回一个准确地值要么就抛出异常
 

hashCode

int hashCode()
返回此 BigDecimal 的哈希码
数值上相等但标度不同的两个 BigDecimal 对象(如,2.0 和 2.00)通常没有 相同的哈希码
 

toString

toString() 返回字符串表示形式,如果需要指数,则使用科学记数法
toEngineeringString() 返回字符串表示形式,需要指数时,则使用工程计数法
toPlainString()  返回不带指数字段的此 BigDecimal 的字符串表示形式
toString的三个方法根本逻辑是一样的,都是转换为字符串
只不过具体的形式不同

 

ulp

unit in the last place
两个数之间的距离,在数学中是无限的,比如1和2之间有无数个数
但是在计算机中是有限的,因为计算机需要用有限个字节来表示double或者float,计算机表示不了无限的数
因为没有无限内存
假设两个数之间有10个数,那么ulp 就是1/10 
1和2之间有一个数 距离为1
1.1和2.1之间有十个数  距离为0.1
这就是ulp
 
非零 BigDecimal 值的 ulp 是此值与下一个具有相同位数的较大 BigDecimal 值之间的正距离
零值的 ulp 在数值上等于1 和 this.scale()之间的距离
所以可以说所有的数的ulp为[1, this.scale()]
image_5bdbee2a_4d96
 

移动小数点

movePointLeft
该值的小数点向左移动 n 位
如果 n 为负数,则该调用等效于 movePointRight(-n)
如果 n 为非负数,则调用仅将 n 添加到该标度
返回的标度分别为:
image_5bdbee2a_2cfe
image_5bdbee2a_5e59
movePointRight
小数点向右移动 n 位
如果 n 为负,则该调用等效于 movePointLeft(-n)
如果 n 为非负数,则该调用仅从该标度减去 n
返回的标度分别为:
image_5bdbee2a_6782
image_5bdbee2a_1089
 
BigDecimal scaleByPowerOfTen(int n)
返回其数值等于
image_5bdbee2a_3358
的BigDecimal

该结果的标度为:
image_5bdbee2a_6ef1

BigDecimal stripTrailingZeros()
形式转换,数值是相等的
转换为去掉所有尾部的0的形式的数值
800.000去掉所有的0 就是8   准换后为8乘以10的平方
 
 

总结

BigDecimal虽然有诸多特性与特别,,但是本质仍旧是浮点数
所以自然提供了浮点数相关的一些操作
作为数值的基本运算方法都具备的
需要注意的是构造方法之间的区别
除非特别需要,否则不要直接使用double构造
尽可能的使用String的形式
对于valueOf方法也是具有缓存的
BigDecimal是不可变的
setScale的名字起的不太规范,容易让人迷惑,使用时要注意。
 
 

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/15620.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论