BigDecimal

不精确的情况:

public class BigDecimalDemo1 {
public static void main(String[] args) {
System.out.println(0.09 + 0.01); // 0.09999999999999999
System.out.println(0.216 - 0.1); // 0.11599999999999999
System.out.println(0.226 * 0.01); // 0.0022600000000000003
System.out.println(0.09 / 0.1); // 0.8999999999999999
}
}

有些小数可能有很多位:


图1

浮点类型所占用的位数是有限的:


图2

如果浮点数的二进制位数太多, 超过了位数的限制, 则只能舍弃, 从而导致数据不精确.

BigDecimal 所在的包: java.math


图3

程序示例:

public class Demo {
public static void main(String[] args) {
/*
构造方法获取 BigDecimal 对象
public BigDecimal(double val)
public BigDecimal(String val)
静态方法获取 BigDecimal 对象
public static BigDecimal valuef(double val)
*/
// 1. 通过传递 double 类型的小数来创建对象
// 细节:
// 这种方式有可能是不精确的, 所以不建议使用
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal(0.09);
System.out.println(bd1); // 0.01000000000000000020816681711721685132943093776702880859375
System.out.println(bd2); // 0.0899999999999999966693309261245303787291049957275390625
// 通过传递字符串表示的小数来创建对象
BigDecimal bd3 = new BigDecimal("0.01");
BigDecimal bd4 = new BigDecimal("0.09");
BigDecimal bd5 = bd3.add(bd4);
System.out.println(bd3); // 0.01
System.out.println(bd4); // 0.09
System.out.println(bd5); // 0.10
}
}

图3


图4

程序示例:

public class Demo {
public static void main(String[] args) {
/*
构造方法获取 BigDecimal 对象
public BigDecimal(double val)
public BigDecimal(String val)
静态方法获取 BigDecimal 对象
public static BigDecimal valueOf(double val)
*/
// 3. 通过静态方法获取对象
// 细节:
// 1. 如果要表示的数字不大, 没有超出 double 的取值范围, 建议使用静态方法
// 2. 如果要表示的数字比较大, 超出了 double 的取值范围, 建议使用构造方法
// 3. 如果我们传递的是 0 ~ 10 之间的整数, 包含 0, 包含 10, 那么方法会返回已经创建好的对象, 不会重新 new
BigDecimal bd6 = BigDecimal.valueOf(10.0);
BigDecimal bd7 = BigDecimal.valueOf(10.0);
System.out.println(bd6 == bd7); // false
BigDecimal bd66 = BigDecimal.valueOf(10);
BigDecimal bd77 = BigDecimal.valueOf(10);
System.out.println(bd66 == bd77); // true
}
}

参数为 double 的 valueOf() 方法:


图4

把传递进来的参数, 先变为字符串, 再传递给构造方法.

参数为 long 的 valueOf() 方法:


图5

查看 ZERO_THROUGH_TEN 数组:


图6

从 0 到 10 都建立了一个对象, 放到了数组中, 因此这个数组长度为 11.

BigDecimal 的成员方法:


图7

程序示例:

import java.math.BigDecimal;
import java.math.RoundingMode;
public class Demo {
public static void main(String[] args) {
/*
public BigDecimal add(BigDecimal val) // 加法
public BigDecimal subtract(BigDecimal val) // 减法
public BigDecimal multiply(BigDecimal val) // 乘法
public BigDecimal divide(BigDecimal val) // 除法
public BigDecimal divide(BigDecimal val, 精确几位, 舍入模式) // 除法
*/
// 1. 加法
BigDecimal bd1 = BigDecimal.valueOf(10.);
BigDecimal bd2 = BigDecimal.valueOf(3.0);
BigDecimal bd3 = bd1.add(bd2);
System.out.println(bd3); // 12.0
// 2. 减法
BigDecimal bd4 = bd1.subtract(bd2);
System.out.println(bd4); // 8.0
// 3. 乘法
BigDecimal bd5 = bd1.multiply(bd2);
System.out.println(bd5); // 20.00
// 4. 除法
BigDecimal bd6 = bd1.divide(bd2, 2, RoundingMode.HALF_UP); // 四舍五入
BigDecimal bd7 = bd1.divide(bd2, 2, BigDecimal.ROUND_HALF_UP); // 废弃的写法
System.out.println(bd6); // 3.33
System.out.println(bd7); // 3.33
}
}

如果有一个小数, 转位二进制之后, 有几百位甚至几千位, 那么用这种分段储存到数组的方法, 效率就较低. 所以 BigDecimal 采取了另外一种方式. 在获取 BigDecimal 对象时, 不论是用 valueOf 的方式获取的, 还是用 new 通过构造方法获取, 其实最终都是 new 出来的, 参数都是一个字符串形式的小数. Java 拿到这个字符串之后, 会遍历这个字符串, 得到里面的每一个字符, 再将每一个字符都转换为对应的 ASCII 码表的数值, 再存储到数组中.

所以说, BigDecimal 在底层是一个数组, 数组中存储的是每一个字符在 ASCII 码表中对应的数字.


图8

图9

用打断点的方式查看:

public class Demo4 {
public static void main(String[] args) {
BigDecimal bd1 = BigDecimal.valueOf(0.226);
BigDecimal bd2 = BigDecimal.valueOf(123.226);
BigDecimal bd3 = BigDecimal.valueOf(-1.5);
}
}

图10

注意这些数组是 byte 类型的.

BigDecimal 也是有上限的, 因为数组最大长度是 int 类型的最大值. 但是实际电脑内存是不可能达到这么大的值的, 所以可以认为是无限大的.

posted @   有空  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示