当前位置:网站首页>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);
}
}
边栏推荐
- uva 1401 dp+Trie
- Clickhouse fields are grouped and aggregated, and SQL is queried according to the granularity of any time period
- C language instance_ five
- Can't you understand the code of linked list in C language? An article allows you to grasp the secondary pointer and deeply understand the various forms of parameter passing in the function parameter
- How can I code for 8 hours without getting tired.
- AcWing 904. Wormhole solution (SPFA for negative rings)
- Recognition of C language array
- 图片打水印 缩放 和一个输入流的转换
- C language [23] classic interview questions [Part 2]
- New job insights ~ leave the old and welcome the new~
猜你喜欢
Let's see how to realize BP neural network in Matlab toolbox
AcWing 345. 牛站 题解(floyd的性质、倍增)
LeetCode:1175. Prime permutation
刨析《C语言》【进阶】付费知识【一】
Yunna | work order management measures, how to carry out work order management
tansig和logsig的差异,为什么BP喜欢用tansig
Baidu flying general BMN timing action positioning framework | data preparation and training guide (Part 1)
Appium自动化测试基础 — uiautomatorviewer定位工具
今日问题-2022/7/4 lambda体中修改String引用类型变量
AcWing 1148. Secret milk transportation problem solution (minimum spanning tree)
随机推荐
Telnet,SSH1,SSH2,Telnet/SSL,Rlogin,Serial,TAPI,RAW
WCF基金会
使用nodejs完成判断哪些项目打包+发版
PartyDAO如何在1年内把一篇推文变成了2亿美金的产品DAO
Share a general compilation method of so dynamic library
When grep looks for a process, it ignores the grep process itself
Yunna - work order management system and process, work order management specification
Use nodejs to determine which projects are packaged + released
7.6 simulation summary
公钥\私人 ssh避password登陆
Long press the button to execute the function
ROS学习(25)rviz plugin插件
DS-5/RVDS4.0变量初始化错误
Amway wave C2 tools
WCF Foundation
AcWing 904. 虫洞 题解(spfa求负环)
1123. The nearest common ancestor of the deepest leaf node
Basic introduction and use of dvajs
AcWing 1140. Shortest network (minimum spanning tree)
json学习初体验–第三者jar包实现bean、List、map创json格式