当前位置:网站首页>BigDecimal 的正确使用方式
BigDecimal 的正确使用方式
2022-07-06 18:16:00 【Dily_Su】
一、背景
BigDecimal 平时主要用于计算金钱时,其自身提供了很多的构造方法,但是这些构造方法使用不当会造成精度丢失,从而引起事故。
二、事故案例
1、问题
收银台计算商品价格报错,导致订单无法支付
2、问题复现
public static void main(String[] args) {
BigDecimal bigDecimal=new BigDecimal(88);
System.out.println(bigDecimal);
bigDecimal=new BigDecimal("8.8");
System.out.println(bigDecimal);
bigDecimal=new BigDecimal(8.8);
System.out.println(bigDecimal);
}

3、源码分析
public static long doubleToLongBits(double value) {
long result = doubleToRawLongBits(value);
// Check for NaN based on values of bit fields, maximum
// exponent and nonzero significand.
if ( ((result & DoubleConsts.EXP_BIT_MASK) ==
DoubleConsts.EXP_BIT_MASK) &&
(result & DoubleConsts.SIGNIF_BIT_MASK) != 0L)
result = 0x7ff8000000000000L;
return result;
}
问题就处在 doubleToRawLongBits 这个方法上,在 jdk 中 double 类(float 与 int 对应)中提供了 double 与 long 转换,doubleToRawLongBits 就是将 double 转换为 long,这个方法是原始方法(底层不是 java 实现,是 c++ 实现的)。
4、原因分析
在 java 中 BigDecimal 处理数据时把十进制小数扩大 N 倍让它在整数上进行计算,并保留相应的精度信息。
- float 和 double 类型,主要是为了科学计算和工程计算而设计的,之所以执行二进制浮点运算,是为了在广泛的数值范围上提供较为精确的快速近和计算。
- 并没有提供完全精确的结果,所以不应该被用于精确的结果的场合。
- 当浮点数达到一定大的数,就会自动使用科学计数法,这样的表示只是近似真实数而不等于真实数。
- 当十进制小数位转换二进制的时候也会出现无限循环或者超过浮点数尾数的长度。
三、总结
在设计到精度计算时,我们尽量使用 String 类型来进行转换,而且涉及到 BigDecimal 的计算,要使用其对应方法进行计算。
四、工具类
这里封装一个 BigDecimal 工具类
public class BigDecimalUtils {
/** * double 加 * * @param v1 加数 * @param v2 加数 * @return 和 */
public static BigDecimal doubleAdd(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2);
}
/** * float 加 * * @param v1 加数 * @param v2 加数 * @return 和 */
public static BigDecimal floatAdd(float v1, float v2) {
BigDecimal b1 = new BigDecimal(Float.toString(v1));
BigDecimal b2 = new BigDecimal(Float.toString(v2));
return b1.add(b2);
}
/** * double 减 * * @param v1 被减数 * @param v2 减数 * @return 差 */
public static BigDecimal doubleSub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2);
}
/** * float 减 * * @param v1 被减数 * @param v2 减数 * @return 差 */
public static BigDecimal floatSub(float v1, float v2) {
BigDecimal b1 = new BigDecimal(Float.toString(v1));
BigDecimal b2 = new BigDecimal(Float.toString(v2));
return b1.subtract(b2);
}
/** * double 乘 * * @param v1 因数 * @param v2 因数 * @return 积 */
public static BigDecimal doubleMul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2);
}
/** * float 乘 * * @param v1 因数 * @param v2 因数 * @return 积 */
public static BigDecimal floatMul(float v1, float v2) {
BigDecimal b1 = new BigDecimal(Float.toString(v1));
BigDecimal b2 = new BigDecimal(Float.toString(v2));
return b1.multiply(b2);
}
/** * double 除 * * @param v1 被除数 * @param v2 除数 * @return 商 */
public static BigDecimal doubleDiv(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
// 保留小数点后两位 ROUND_HALF_UP = 四舍五入
return b1.divide(b2, 2, RoundingMode.HALF_UP);
}
/** * float 除 * * @param v1 被除数 * @param v2 除数 * @return 商 */
public static BigDecimal floatDiv(float v1, float v2) {
BigDecimal b1 = new BigDecimal(Float.toString(v1));
BigDecimal b2 = new BigDecimal(Float.toString(v2));
// 保留小数点后两位 ROUND_HALF_UP = 四舍五入
return b1.divide(b2, 2, RoundingMode.HALF_UP);
}
/** * double<br> * 比较v1 v2大小 * * @param v1 * @param v2 * @return v1>v2 return 1 v1=v2 return 0 v1<v2 return -1 */
public static int doubleCompareTo(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.compareTo(b2);
}
/** * float<br> * 比较v1 v2大小 * * @param v1 * @param v2 * @return v1>v2 return 1 v1=v2 return 0 v1<v2 return -1 */
public static int floatCompareTo(float v1, float v2) {
BigDecimal b1 = new BigDecimal(Float.toString(v1));
BigDecimal b2 = new BigDecimal(Float.toString(v2));
return b1.compareTo(b2);
}
}
边栏推荐
- Yunna | work order management measures, how to carry out work order management
- AcWing 904. 虫洞 题解(spfa求负环)
- 一文带你走进【内存泄漏】
- Match VIM from zero (0) -- Introduction to vimscript
- PartyDAO如何在1年内把一篇推文变成了2亿美金的产品DAO
- C language instance_ five
- 糊涂工具类(hutool)post请求设置body参数为json数据
- Domestic images of various languages, software and systems. It is enough to collect this warehouse: Thanks mirror
- Let's see how to realize BP neural network in Matlab toolbox
- Set WordPress pseudo static connection (no pagoda)
猜你喜欢
随机推荐
AcWing 344. 观光之旅题解(floyd求无向图的最小环问题)
Ds-5/rvds4.0 variable initialization error
编译命令行终端 swift
json学习初体验–第三者jar包实现bean、List、map创json格式
Basic introduction and use of dvajs
MySQL's most basic select statement
AcWing 346. Solution to the problem of water splashing festival in the corridor (deduction formula, minimum spanning tree)
LeetCode. Sword finger offer 62 The last remaining number in the circle
场景实践:基于函数计算快速搭建Wordpress博客系统
Get to know MySQL for the first time
7.6 simulation summary
MySQL最基本的SELECT(查询)语句
Appium automation test foundation uiautomatorviewer positioning tool
Transplant DAC chip mcp4725 to nuc980
Compile command line terminal swift
字符串转成日期对象
Let's see how to realize BP neural network in Matlab toolbox
The difference between Tansig and logsig. Why does BP like to use Tansig
DS-5/RVDS4.0变量初始化错误
POJ 3177 redundant paths POJ 3352 road construction (dual connection)









