java中的BigDecimal使用

本文详细介绍了BigDecimal的概念、使用场景及其核心操作方法,包括初始化、四则运算、小数位控制与数值比较等。

1、什么是BigDecimal?

BigDecimal属于java.math包,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。

float和double只能用来做科学计算或者是工程计算,在商业计算中(如金融行业)要用java.math.BigDecimal。

BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

2、为什么使用BigDecimal?

我们看如下代码:

public static void main(String[] args) throws Exception {
        System.out.println(0.2 + 0.1);
        System.out.println(0.3 - 0.1);
        System.out.println(0.2 * 0.1);
        System.out.println(0.3 / 0.1);
    }

打印结果:

在这里插入图片描述
为何会这样?

原因:不论是float 还是double都是浮点数,而计算机是二进制的,浮点数会失去一定的精确度。

根本原因:十进制值通常没有完全相同的二进制表示形式;十进制数的二进制表示形式可能不精确。只能无限接近于那个值。

但是,在项目中,我们不可能让这种情况出现,特别是金融项目,因为涉及金额的计算都必须十分精确,否则,如果你的微信账户余额显示193.99999999999998,那是一种怎么样的体验?

3、如何使用BigDecimal?

(1)BigDecimal初始化赋值

方法类型描述
public BigDecimal(int val)构造函数int类型的值生成BigDecimal对象
public BigDecimal(long val)构造函数long类型的值生成BigDecimal对象
public BigDecimal(String val)静态方法String类型的值转换为BigDecimal类型
public static BigDecimal valueOf(double val)静态方法double类型的值转换为BigDecimal类型
public static BigDecimal valueOf(long val)静态方法long类型(包含int类型)的值转换为BigDecimal类型

代码示例:

 		BigDecimal b = new BigDecimal("25");
        BigDecimal c = new BigDecimal(12);

        System.out.println(b);
        System.out.println(c);

打印结果:

在这里插入图片描述

注意:不建议使用public BigDecimal(double val)方式初始化值

如下代码:

		BigDecimal c = new BigDecimal(12.3);
        System.out.println(c);

打印结果:

在这里插入图片描述
如图,也会出现计算的错误。

通常建议优先使用String构造方法。当double必须用作BigDecimal的源时,请使用Double.toString(double)转成String,然后使用String构造方法,或使用BigDecimal的静态方法valueOf:

		BigDecimal b = new BigDecimal("12.3");
        BigDecimal c = BigDecimal.valueOf(7.34325);
        BigDecimal d = new BigDecimal(Double.toString(7.34325));
        System.out.println(b);
        System.out.println(c);
        System.out.println(d);

打印结果:

在这里插入图片描述

(2)加减乘除运算

运算法则对应方法
加法public BigDecimal add(BigDecimal value)
减法public BigDecimal subtract(BigDecimal value)
乘法public BigDecimal multiply(BigDecimal value)
除法public BigDecimal divide(BigDecimal value)

代码示例:

public static void main(String[] args) throws Exception {
        System.out.println("计算加法: " + BigDecimal.valueOf(1.9).add(BigDecimal.valueOf(0.2)));
        System.out.println("计算减法: " + BigDecimal.valueOf(1.9).subtract(BigDecimal.valueOf(1.5)));
        System.out.println("计算乘法: " + BigDecimal.valueOf(1.9).multiply(BigDecimal.valueOf(0.2)));
        System.out.println("计算除法: " + BigDecimal.valueOf(1.9).divide(BigDecimal.valueOf(0.2)));
    }

打印结果:

在这里插入图片描述
注意点:

  1. BigDecimal的运算结果都是返回了一个新的BigDecimal对象,并不是在原有的对象上进行操作。

代码示例:

public static void main(String[] args)  {
        BigDecimal bigDecimal = new BigDecimal("2");
        System.out.println("bigDecimal计算前的地址是:"+System.identityHashCode(bigDecimal));
        bigDecimal = bigDecimal.add(BigDecimal.valueOf(3));
        System.out.println("bigDecimal计算后的地址是:"+System.identityHashCode(bigDecimal));
    }

打印结果:
在这里插入图片描述

  1. 使用divide除法函数除不尽,出现无线循环小数的时候,就需要使用另外精确的小数位数以及舍入模式,不然会出现报错。

代码示例:

public static void main(String[] args)  {
        BigDecimal a = BigDecimal.valueOf(10), b = BigDecimal.valueOf(3);
        System.out.println(a.divide(b));
    }

报错如下:

在这里插入图片描述

解决方法可以采用四舍五入的方式,详情见下文!

(3)BigDecimal保留两位小数及舍入模式

public BigDecimal setScale(int newScale, int roundingMode)

用于格式化小数的方法,第一个值表示保留几位小数,第二个值表示格式化的类型。
8种类型如下:

格式化类型描述
ROUND_DOWN舍弃多余位数,如1.55会格式化为1.5,-1.55会格式化为-1.5
ROUND_UP进位处理,如1.52会格式化为1.6,-1.52会格式化为-1.6
ROUND_HALF_UP四舍五入,如果舍弃部分>= .5,则进位
ROUND_HALF_DOWN五舍六入,如果舍弃部分> .5,则进位
ROUND_CEILING正无穷大方向舍入模式。如果值为正数,则与ROUND_UP模式相同;如果值为负数,则与ROUND_DOWN模式相同
ROUND_FLOOR负无穷大方向舍入模式。如果值为正数,则与ROUND_DOWN模式相同;如果值为负数,则与ROUND_UP模式相同
ROUND_UNNECESSARY确认值的小数位数是否与传入第一个参数(保留小数的位数)相等,如果符合则返回值,如果不符抛出异常
ROUND_HALF_EVEN如果舍弃部门左边的数字为奇数,则与ROUND_HALF_UP模式相同,如果为偶数则与ROUND_HALF_DOWN模式相同

代码示例:

public static void main(String[] args)  {
        BigDecimal a = BigDecimal.valueOf(5.445);
        System.out.println("5.445舍弃多余位数:" + a.setScale(2, BigDecimal.ROUND_DOWN));
        System.out.println("5.445进位处理:" + a.setScale(2, BigDecimal.ROUND_UP));
        System.out.println("5.445四舍五入(舍弃部分>= .5,进位):" + a.setScale(2, BigDecimal.ROUND_HALF_UP));
        System.out.println("5.445四舍五入(舍弃部分未> .5,舍弃):" + a.setScale(2, BigDecimal.ROUND_HALF_DOWN));
        System.out.println("5.446四舍五入(舍弃部分> .5,进位):" + BigDecimal.valueOf(5.446).setScale(2, BigDecimal.ROUND_HALF_DOWN));
    }

打印结果:
在这里插入图片描述

(4)BigDecimal比较大小

public int compareTo(BigDecimal val)

BigDecimal类提供的比较值的方法,注意比较的两个值均不能为空。
a.compareTo(b)得到结果 1, 0, -1。

比较结果描述
1a 大于b
0a 等于b
-1a 小于b

代码示例:

public static void main(String[] args)  {
        BigDecimal a = BigDecimal.valueOf(1);
        BigDecimal b = BigDecimal.valueOf(2);
        BigDecimal c = BigDecimal.valueOf(1);
        BigDecimal d = BigDecimal.ZERO;
        System.out.println("1和2比较结果:" + a.compareTo(b));
        System.out.println("1和1比较结果:" + a.compareTo(c));
        System.out.println("1和0比较判断:" + (a.compareTo(d) > 0) );
    }

打印结果:
在这里插入图片描述

注意:实际业务中,不会单纯的比较谁打谁小,而是类似于第三条去判断一个布尔值。

(5)BigDecimal其他方法及常量

代码类型描述
BigDecimal.ZERO常量初始化一个为0的BigDecimal对象
BigDecimal.ONE常量初始化一个为1的BigDecimal对象
BigDecimal.TEN常量初始化一个为10的BigDecimal对象
public BigDecimal abs()方法求绝对值,不管正数还是负数,都得到正数
public BigDecimal negate()方法求相反数,正变负,负变正
public BigDecimal pow(int n)方法求乘方,如BigDecimal.valueOf(2).pow(3)的值为8
public BigDecimal max(BigDecimal val)方法两值比较,返回最大值
public BigDecimal min(BigDecimal val)方法两值比较,返回最小值
<think>首先,用户的问题是:“我想了解JavaBigDecimal的存储方式,并询问金融数据的推荐存储方式 请问Java BigDecimal 存储方式 金融数据存储推荐”。这涉及到两个部分:Java BigDecimal的存储方式,以及金融数据存储的推荐方式。 我需要参考系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1] 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 参考站内引用: - 引用[1]: 提到BigDecimal在金融行业中的使用,因为float和double不适合货币计算。 - 引用[2]: 提到BigDecimal的显示方法。 - 引用[3]: 强调金融数据必须使用BigDecimal,并展示了double和BigDecimal的精度问题。 我的回答需要覆盖: 1. Java BigDecimal的存储方式。 2. 金融数据存储的推荐方式。 结构: - 先回答BigDecimal的存储方式。 - 然后讨论金融数据存储的推荐。 - 最后生成相关问题。 BigDecimal的存储方式: - BigDecimalJava中是一个类,用于表示任意精度的十进制数。 - 它内部使用一个BigInteger来表示未缩放的值(unscaled value),和一个int来表示缩放因子(scale)。 - 具体来说:BigDecimal对象包含两个主要字段: - intVal: 一个BigInteger,表示整数部分。 - scale: 一个int,表示小数点的位置(即小数点后的位数)。 - 例如,数字123.45可以表示为unscaled value = 12345 和 scale = 2。 - 这允许精确的十进制运算,避免了浮点数的精度问题。 金融数据存储推荐: - 根据引用,金融数据应该使用BigDecimal,而不是float或double,因为浮点数有精度问题。 - 在存储时,推荐使用BigDecimal来表示金额。 - 在数据库中,可以存储为DECIMAL或NUMERIC类型,这些类型支持固定精度和小数点。 - 在通信接口中,如引用[1]所说,可以使用String来表示金额,但内部处理时最好用BigDecimal。 - 最佳实践是:在Java代码中使用BigDecimal,在数据库中存储为DECIMAL,在JSON等数据交换格式中,可以序列化为String或使用字符串表示。 使用中文回答: - 整个回答要用中文。 生成相关问题: - 在回答末尾,添加“相关问题”部分,列出2-3个相关问题。 引用标识: - 在回答中引用站内内容时,添加[^n]标识。 - 例如,提到精度问题时,引用[1]或[3]。 回答结构: 1. 介绍BigDecimal及其重要性。 2. 详细解释BigDecimal的存储方式。 3. 讨论金融数据存储的推荐方式。 4. 总结。 5. 添加相关问题。 确保LaTeX正确: - 行内数学表达式用$...$,如$123.45$。 - 独立公式用$$...$$,但可能不需要,除非有复杂公式。 现在,构建回答: - 开头:直接回应用户问题。 - 主体: - 第一部分:BigDecimal存储方式。 - 第二部分:金融数据存储推荐。 - 结尾:相关问题。</think>### Java BigDecimal 存储方式与金融数据存储推荐 在 Java 中,`BigDecimal` 类用于处理高精度的十进制数,特别适合金融计算,因为它避免了浮点数的精度问题(如 `float` 或 `double` 类型)。下面我将逐步解释其存储方式,并推荐金融数据的存储方法。 #### 1. **Java BigDecimal 的存储方式** `BigDecimal` 的内部实现基于两个核心组件:一个未缩放值(`unscaled value`)和一个缩放因子(`scale`)。具体来说: - **未缩放值(`unscaled value`)**:这是一个 `BigInteger` 对象,表示整数值部分。例如,对于数字 $123.45$,未缩放值就是 $12345$(去除小数点后的整数形式)。 - **缩放因子(`scale`)**:这是一个 `int` 值,表示小数点后的位数。例如,$123.45$ 的缩放因子为 $2$,因为小数点后有两位数字。 - **存储机制**:`BigDecimal` 对象在内存中存储为一个元组 `(unscaled value, scale)`。数学上,实际值可表示为: $$ \text{value} = \text{unscaled value} \times 10^{-\text{scale}} $$ 例如,$123.45$ 的存储为 `(12345, 2)`,计算时相当于 $12345 \times 10^{-2}$。 - **优势**:这种方式支持任意精度的十进制运算,避免了浮点数的舍入误差。例如,使用 `double` 计算 $4.0 - 3.6$ 可能得到 $0.3999999999999999$,而 `BigDecimal` 能精确输出 $0.4$(通过字符串构造确保精度)[^3]。 在 Java 源码中,`BigDecimal` 类通过 `BigInteger` 处理大整数运算,并优化了算术操作(如加法、减法),确保金融计算的准确性。存储时,对象本身占用堆内存,大小取决于数字的精度(例如,一个包含 10 位小数的数字可能占用 20-30 字节)。 #### 2. **金融数据存储的推荐方式** 在金融行业中,数据存储必须保证精度和一致性,以避免金额计算错误。以下是基于最佳实践的推荐: - **代码层(Java 应用)**: - **优先使用 `BigDecimal`**:所有涉及金额的变量(如余额、交易额)都应声明为 `BigDecimal` 类型。构造时,**必须使用字符串参数**(如 `new BigDecimal("123.45")`)或 `valueOf` 方法,避免直接使用 `double`(否则会引入精度问题)[^1][^3]。 - **示例代码**: ```java import java.math.BigDecimal; public class FinancialApp { public static void main(String[] args) { // 正确:使用字符串构造BigDecimal BigDecimal amount1 = new BigDecimal("100.50"); BigDecimal amount2 = new BigDecimal("50.25"); BigDecimal total = amount1.add(amount2); // 精确计算为150.75 // 错误:避免使用double构造,否则可能导致精度损失 // BigDecimal wrong = new BigDecimal(100.50); // 不推荐 } } ``` - **数据库层**: - **使用 `DECIMAL` 或 `NUMERIC` 类型**:在 SQL 数据库(如 MySQL、PostgreSQL)中,将金额字段定义为 `DECIMAL(precision, scale)`,其中 `precision` 表示总位数,`scale` 表示小数位数。例如,`DECIMAL(10, 2)` 可存储最大为 $99999999.99$ 的金额。 - **优势**:`DECIMAL` 类型基于定点数实现,与 `BigDecimal` 兼容,能直接映射(通过 JDBC 的 `setBigDecimal` 方法),确保数据存储和检索的精度[^1]。 - **数据交换层(API 通信)**: - **推荐使用字符串序列化**:在 HTTP 接口或 JSON 数据中,将金额表示为字符串(如 `"150.75"`),而不是数字类型。这避免了浮点数在传输中的解析误差。内部处理时,再转换为 `BigDecimal` 进行运算[^1][^2]。 - **格式化显示**:使用 `BigDecimal.stripTrailingZeros().toPlainString()` 方法去除多余零并输出纯字符串,避免科学计数法(如 $2E10$ 变为 $20000000000$)[^2]。 **总结**:`BigDecimal` 的存储基于 `(unscaled value, scale)` 模型,提供高精度计算;金融数据存储应全链路使用精确类型(代码层用 `BigDecimal`,数据库用 `DECIMAL`,传输层用字符串),以消除精度风险。实践中,金融系统如支付网关或会计软件均依赖此方案[^1][^3]。 ### 相关问题 1. `BigDecimal` 在并发环境下如何保证线程安全? 2. 数据库 `DECIMAL` 类型与 `BigDecimal` 如何高效映射? 3. 金融系统中如何处理货币转换时的精度问题?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YD_1989

分享不易,非常感谢您的鼓励支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值