当前位置:网站首页>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);
}
}
边栏推荐
- 盒子拉伸拉扯(左右模式)
- C language instance_ three
- AcWing 361. 观光奶牛 题解(spfa求正环)
- Appium foundation - appium inspector positioning tool (I)
- Share a general compilation method of so dynamic library
- The cradle of eternity
- 永久的摇篮
- Shell script quickly counts the number of lines of project code
- Telnet,SSH1,SSH2,Telnet/SSL,Rlogin,Serial,TAPI,RAW
- AcWing 344. 观光之旅题解(floyd求无向图的最小环问题)
猜你喜欢
Baidu flying general BMN timing action positioning framework | data preparation and training guide (Part 2)
Set WordPress pseudo static connection (no pagoda)
Reptile practice (VI): novel of climbing pen interesting Pavilion
Yunna | work order management measures, how to carry out work order management
刨析《C语言》【进阶】付费知识【一】
Appium基础 — Appium Inspector定位工具(一)
mongodb查看表是否导入成功
AcWing 361. Sightseeing cow problem solution (SPFA seeking positive ring)
C语言关于链表的代码看不懂?一篇文章让你拿捏二级指针并深入理解函数参数列表中传参的多种形式
一文带你走进【内存泄漏】
随机推荐
C language instance_ four
shell脚本快速统计项目代码行数
ROS学习(23)action通信机制
Add PDF Title floating window
AcWing 1140. 最短网络 (最小生成树)
First experience of JSON learning - the third-party jar package realizes bean, list and map to create JSON format
IDEA常用的快捷键
对C语言数组的再认识
tansig和logsig的差异,为什么BP喜欢用tansig
grep查找进程时,忽略grep进程本身
AcWing 1141. 局域网 题解(kruskalkruskal 求最小生成树)
JS reverse -- ob confusion and accelerated music that poked the [hornet's nest]
mysqlbackup 还原特定的表
ROS学习(26)动态参数配置
长按按钮执行函数
Let's see how to realize BP neural network in Matlab toolbox
Set WordPress pseudo static connection (no pagoda)
MySQL最基本的SELECT(查询)语句
AcWing 344. 观光之旅题解(floyd求无向图的最小环问题)
各种语言,软件,系统的国内镜像,收藏这一个仓库就够了: Thanks-Mirror