金额计算在一些支付类项目和电商项目中十分常见,很多开发在日常的写代码中时长会使用加减乘除等计算。
包装类型计算问题
可以看到,两个包装类且值都是200的使用 == 的比较的结果都是false,使用equals比较是true。
Integer源码:
通过Integer的源码可以发现当Integer的范围是 -128~127的时候是直接从缓存里面取的返回值,而超过以后是重新new一个对象返回,当然使用==的时候就会是false,Integer 和 int比较的时候包装类会先转成int 再做比较,所以不会出现false的情况了,同时大家可以看到在编译阶段编辑器就提示了有问题。
开发中可以通过 BigDecimal 进行数值类型的计算。下面会说的
多精度计算问题
可以看到有两个计算出现了精度问题 Java中的简单浮点数类型float和double不能够精确运算。这个问题其实不是JAVA的bug,因为计算机本身是二进制的,而浮点数实际上只是个近似值,所以从二进制转化为十进制浮点数时,精度容易丢失,导致精度下降。
可以看到使用double类型除以0,得到 Infinity(无穷大)。原理是因为java的float和double使用了IEEE 754标准。这个标准规定:浮点数除以0等于正无穷或负无穷。
BigDecimal计算
一般java代码中遇到高精度金额计算,日常使用bigDecimal类型。
在使用BigDecimal类来进行计算的时候,主要分为以下步骤:
1、用float或者double变量构建BigDecimal对象。
2、通过调用BigDecimal的加,减,乘,除等相应的方法进行算术运算。
3、把BigDecimal对象转换成float,double,int等类型。
BigDecimal的compareTo()
BigDecimalA > bigDecimalB时,比较结果为 1
BigDecimalA = bigDecimalB时,比较结果为 0
BigDecimalA < bigDecimalB时,比较结果为 -1
常用的加,减,乘,除,BigDecimal类提供了相应的成员方法。
public BigDecimal add(BigDecimal value); // 加法
public BigDecimal subtract(BigDecimal value); // 减法
public BigDecimal multiply(BigDecimal value); // 乘法
public BigDecimal divide(BigDecimal value); // 除法
下面是一个工具类,该工具类提供加,减,乘,除运算。
public class Arith {
/**
* 提供精确加法计算的add方法
* @param value1 被加数
* @param value2 加数
* @return 两个参数的和
*/
public static double add(double value1,double value2){
BigDecimal b1 = new BigDecimal(Double.valueOf(value1));
BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
return b1.add(b2).doubleValue();
}
/**
* 提供精确减法运算的sub方法
* @param value1 被减数
* @param value2 减数
* @return 两个参数的差
*/
public static double sub(double value1,double value2){
BigDecimal b1 = new BigDecimal(Double.valueOf(value1));
BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精确乘法运算的mul方法
* @param value1 被乘数
* @param value2 乘数
* @return 两个参数的积
*/
public static double mul(double value1,double value2){
BigDecimal b1 = new BigDecimal(Double.valueOf(value1));
BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供精确的除法运算方法div
* @param value1 被除数
* @param value2 除数
* @param scale 精确范围
* @return 两个参数的商
* @throws IllegalAccessException
*/
public static double div(double value1,double value2,int scale) throws IllegalAccessException{
//如果精确范围小于0,抛出异常信息
if(scale<0){
throw new IllegalAccessException("精确度不能小于0");
}
BigDecimal b1 = new BigDecimal(Double.valueOf(value1));
BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
return b1.divide(b2, scale).doubleValue();
}
}
BigDecimal的舍入策略:
ROUND_CEILING:无论后面是多少(后面是0的话除外),在正常值下,往大变
ROUND_FLOOR:无论后面是多少(后面是0的话除外),在正常值下,往小变
ROUND_UP:无论后面是多少(后面是0的话除外),在绝对值的角度,往大变
ROUND_DOWN:无论后面是多少,都直接丢弃
ROUND_HALF_UP:0-4舍,5-9入,在绝对值的角度,舍和入
ROUND_HALF_DOWN:0-5舍,6-9入,在绝对值的角度,舍和入
ROUND_HALF_EVEN:0-4舍,6-9入,;在绝对值的角度舍和入如果是5,则结果为偶数的一边
在开发中为避免计算出现错误,建议使用分来进行存存储,微信的支付单位就分,如果以分为单位就要考虑到数据展示需要转换成 元。
当前共有 0 条评论