【BigDecimal】货币金额和浮点数处理注意事项

【一】【强制】任何货币金额,均以最小货币单位且整型类型来进行存储。

【二】【强制】浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals

来判断。
说明:浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进
制无法精确表示大部分的十进制小数,具体原理参考《码出高效》。
反例:

float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
if (a == b) {
 // 预期进入此代码快,执行其它业务逻辑
 // 但事实上 a==b 的结果为 false
}
Float x = Float.valueOf(a);
Float y = Float.valueOf(b);
if (x.equals(y)) {
 // 预期进入此代码快,执行其它业务逻辑
 // 但事实上 equals 的结果为 false
}

正例:
1-指定一个误差范围,两个浮点数的差值在此范围之内,则认为是相等的。

float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
float diff = 1e-6f;
if (Math.abs(a - b) < diff) {
 System.out.println("true");
}

2-使用 BigDecimal 来定义值,再进行浮点数的运算操作。

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
if (x.equals(y)) {
 System.out.println("true");
}

【三】double类型转BigDecimal

【强制】禁止使用构造方法 BigDecimal(double)的方式把 double 值转化为 BigDecimal 对象。
说明:BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。
如:BigDecimal g = new BigDecimal(0.1f); 实际的存储值为:0.10000000149
正例:优先推荐入参为 String 的构造方法,或使用 BigDecimal 的 valueOf 方法,此方法内部其实执行了
Double 的 toString,而 Double 的 toString 按 double 的实际能表达的精度对尾数进行了截断。

 BigDecimal recommend1 = new BigDecimal("0.1");
 BigDecimal recommend2 = BigDecimal.valueOf(0.1);

【四】BigDecimal转double

不要直接使用Double.parseDouble转,可以先把BigDecimal转成String类型,然后再使用parseDouble转成double类型

Double.parseDouble(tkjToCnyRate.toPlainString())

【五】BigDecimal的divide方法

使用divide方法也就是除法,使用除法就可能会产生小数,这个小数需要进行处理,例如设置保留位数以及取整方法等等。

divide(percent)

如果直接使用会报错:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

正确用法:除数是percent,小数点后保留8位,并且进行向上取整

divide(percent,8, RoundingMode.HALF_UP);

【六】BigDecimal的加减乘除运算方法

【1】BigDecimal的加法运算

BigDecimal的加法运算方法为add(),其语法如下:

public BigDecimal add(BigDecimal augend)

其中,augend表示被加数,即要加的数。add()方法将augend与当前BigDecimal对象相加,并返回一个新的BigDecimal对象,表示它们的和。

例如,假设有两个BigDecimal对象a和b,它们的值分别为1.23和4.56,那么它们的和可以通过以下代码计算:

BigDecimal a = new BigDecimal("1.23");
BigDecimal b = new BigDecimal("4.56");
BigDecimal sum = a.add(b);

在上述代码中,sum表示a和b的和,其值为5.79。

【2】BigDecimal的减法运算

BigDecimal的减法运算方法为subtract(),其语法如下:

public BigDecimal subtract(BigDecimal subtrahend)

其中,subtrahend表示被减数,即要减去的数。subtract()方法将subtrahend从当前BigDecimal对象中减去,并返回一个新的BigDecimal对象,表示它们的差。

例如,假设有两个BigDecimal对象a和b,它们的值分别为4.56和1.23,那么它们的差可以通过以下代码计算:

BigDecimal a = new BigDecimal("4.56");
BigDecimal b = new BigDecimal("1.23");
BigDecimal diff = a.subtract(b);

在上述代码中,diff表示a和b的差,其值为3.33。

【3】BigDecimal的乘法运算

BigDecimal的乘法运算方法为multiply(),其语法如下:

public BigDecimal multiply(BigDecimal multiplicand)

其中,multiplicand表示乘数,即要乘的数。multiply()方法将当前BigDecimal对象乘以multiplicand,并返回一个新的BigDecimal对象,表示它们的积。

例如,假设有两个BigDecimal对象a和b,它们的值分别为1.23和4.56,那么它们的积可以通过以下代码计算:

BigDecimal a = new BigDecimal("1.23");
BigDecimal b = new BigDecimal("4.56");
BigDecimal product = a.multiply(b);

在上述代码中,product表示a和b的积,其值为5.6088。

【4】BigDecimal的除法运算

BigDecimal的除法运算方法为divide(),其语法如下:

public BigDecimal divide(BigDecimal divisor)

其中,divisor表示除数,即要除以的数。divide()方法将当前BigDecimal对象除以divisor,并返回一个新的BigDecimal对象,表示它们的商。需要注意的是,如果除不尽,那么divide()方法将会抛出ArithmeticException异常。

例如,假设有两个BigDecimal对象a和b,它们的值分别为4.56和1.23,那么它们的商可以通过以下代码计算:

BigDecimal a = new BigDecimal("4.56");
BigDecimal b = new BigDecimal("1.23");
BigDecimal quotient = a.divide(b, 2, RoundingMode.HALF_UP);

在上述代码中,quotient表示a和b的商,其值为3.71。其中,第二个参数2表示保留两位小数,第三个参数RoundingMode.HALF_UP表示采用四舍五入的方式进行舍入。

【七】BigDecimal 转字符串,并去掉尾部的0

【1】有一种写法,先转成Double

BigDecimal target = new BigDecimal("5375130.000000");
BigDecimal.valueOf(Double.parseDouble(target.toString())).toString()

这种写法发现两个问题:
1、小于8位数时,可以正常转化,但如果是整数的话,后边会带着一个".0"。
2、大于等于8位数时,如果是10的倍数的话,转化的还是科学计数法类型的;如果不是的话,可以正常转化,后边不会带着“.0”。
测试代码:

BigDecimal target = new BigDecimal("5375130.000000");
String str = BigDecimal.valueOf(Double.parseDouble(target.toString())).toString();
System.out.println("小于8位,并且是整数:" + str);
System.out.println("======================");
target = new BigDecimal("12676490.000000");
str = BigDecimal.valueOf(Double.parseDouble(target.toString())).toString();
System.out.println("大于等于8位,并且是10的倍数" + str);
System.out.println("======================");
target = new BigDecimal("12676491.000000");
str = BigDecimal.valueOf(Double.parseDouble(target.toString())).toString();
System.out.println("大于等于8位,不是10的倍数" + str);

执行结果:
在这里插入图片描述

【2】另一种方式,应用BigDecimal自身的方法

stripTrailingZeros() 去掉尾部的0;
toPlainString() 转化字符串,非科学计数法。

BigDecimal target = new BigDecimal("5375130.000000");
String str = target.stripTrailingZeros().toPlainString();

在这里插入图片描述

【八】BigDecimal使用(小数点,字符串等转换)

【1】BigDecimal介绍

Java中提供了操作大数字(超过16位有效位)的类,即 java.math.BigInteger 类和 java.math.BigDecimal 类,用于高精度计算。

float和Double只能用来做科学计算、工程计算等;在商业计算中,对数字精度要求较高(例如货币值),必须使用 BigInteger 类和 BigDecimal 类,它支持任何精度的定点数,可以用它来精确计算。

BigDecimal类创建的是对象,不能使用传统的 +、-、*、/ 等算术运算符进行数学运算,而必须调用它的方法。方法的参数也必须是BigDecimal类型的对象。

【2】数字转换类型问题

java中,BigDecimal十分好用。各种格式的数字只要往BigDecimal里无脑丢即可。例如:

//字符串型
String num1 = "10.0";
BigDecimal d1 = new BigDecimal(num1);
int i1 = d1.intValue();

例如,别的系统给你传数据,发过来的都是字符串,其中有个字段是数量。但对方传过来的是"10.0",如果用 Integer.parseInt(num1),就会报错如下:java.lang.NumberFormatException: For input string: “10.0”

如果要处理这种情况,按照传统的方法,就得加很多校验,判断是否有小数点,等等。但如果用BigDecimal,就很舒服。

【3】BigDecimal常见问题

(1)new Decimal时不允许传double
new BigDecimal()的时候,不允许传入double类型的数,通常传入String类型的数(推荐这样)。因为double类型的数传入BigDecimal无法得到精确值。

BigDecimal b1 = new BigDecimal(0.1);    //不允许
BigDecimal b2 = new BigDecimal("0.1");  //推荐

不允许传入double类型的原因:

BigDecimal b10 = new BigDecimal(0.1);
BigDecimal b11 = new BigDecimal(1.1);
System.out.println(b10.add(b11).doubleValue());    //输出1.2000000000000002

BigDecimal b12 = new BigDecimal("0.1");
BigDecimal b13 = new BigDecimal("1.1");
System.out.println(b12.add(b13).doubleValue());    //输出1.2

如果值是double类型, 就需要先转换成String,再传入。也可以使用 valueOf(),用法如下

BigDecimal.valueOf(0.1)

它的源码其实就是将double转string:

public static BigDecimal valueOf(double val) {
    return new BigDecimal(Double.toString(val));
}

【4】工具类

public class Arith {
 
    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();
    }
    
 
    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();
    }
    
 
    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();
    }
    
 
    public static double div(double value1,double value2,int scale) throws IllegalAccessException{
        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();    
    }
}

【5】保留小数位

ROUND_DOWN --> 直接删除多余的小数位 ,(这种方式得到的绝对值不会比原数大)
ROUND_UP --> 在最后一位直接加1,
ROUND_CEILING --> 正数时与ROUND_UP一致,负数时与ROUND_DOWN一致
ROUND_FLOOR --> 正数时与Round_DOWN一致,负数时与ROUND_UP一致
ROUND_HALF_UP --> 四舍五入
ROUND_HALF_DOWN --> 五舍六入
ROUND_HALE_EVEN --> 四舍六入五看奇进偶不进(四舍六入五成双)

【九】BigDecimal开根号

BigDecimal没有开根号的方法,可以先转成double类型,然后再使用Math的开根号方法,最后转成BigDecimal

BigDecimal.valueOf(Math.sqrt(thisCycleDataSquared.doubleValue())
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值