[十七]基础类型BigDecimal简介
BigDecimal是不可变的、任意精度的、有符号的、十进制数.
组成部分
BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度 (scale) 组成 |
BigDecimal 表示的数值是 :
unscaledValue × 10的-scale 次幂
|
私有成员intVal就是非标度值
scale就是标度
标度
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(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 一样
当 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
常用方法
属性获取
四则运算
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 有影响的是精度
注意
如果同时需要整数商和余数
则divideAndRemainder比分别使用 divideToIntegralValue 和 remainder 方法更快速,因为相除仅需执行一次
remainder则是依赖于divideAndRemainder ,然后返回的第二个元素
数学方法
equals
判断是否相等
与 compareTo 不同
仅当两个 BigDecimal 对象的值和标度都相等时,此方法才认为它们相等
(因此通过此方法进行比较时,2.0 不等于 2.00)
|
一定要注意到compareTo方法与equals方法 对于相等的定义是不一致的
valueOf
setScale
setScale 系列并不是设置BigDecimal的scale BigDecimal是不可变得
setScale 是一个转换器,将参数的BigDecimal转换为指定标度的值
值本身不会变化,变化的是形式
返回的是一个新的BigDecimal,不过这个新的BigDecimal并不一定是新创建的
可能是使用缓存,新是相对于调用者来说
方法列表:
negate/plus/round
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
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()]
移动小数点
movePointLeft 该值的小数点向左移动 n 位 如果 n 为负数,则该调用等效于 movePointRight(-n) 如果 n 为非负数,则调用仅将 n 添加到该标度 返回的值和标度分别为: |
movePointRight 小数点向右移动 n 位 如果 n 为负,则该调用等效于 movePointLeft(-n) 如果 n 为非负数,则该调用仅从该标度减去 n 返回的值和标度分别为: |
BigDecimal stripTrailingZeros() 形式转换,数值是相等的 转换为去掉所有尾部的0的形式的数值 800.000去掉所有的0 就是8 准换后为8乘以10的平方 |
总结
BigDecimal虽然有诸多特性与特别,,但是本质仍旧是浮点数
所以自然提供了浮点数相关的一些操作
作为数值的基本运算方法都具备的
需要注意的是构造方法之间的区别
除非特别需要,否则不要直接使用double构造
尽可能的使用String的形式
对于valueOf方法也是具有缓存的
BigDecimal是不可变的
setScale的名字起的不太规范,容易让人迷惑,使用时要注意。