前提:涉及到了浮点数的计算问题时,众所周知,浮点数无法用于精确计算,得到的结果经常会小数位特别多,不符合预期结
果。float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供
较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。同时,
出于某些需求,思考以下几个问题:
1. 对浮点数取小数点后取指定N位数
2. 判断浮点数的小数位数
试图通过取余取商等一些计算的方式解决上述两个问题都是不可取的,参与计算就可能导致结果不精确,得出错误结论。
对于问题1,解决方式主要有以下几种
(关于DecimalFormatter的默认舍入模式,为ROUND_HALF_EVEN,指定某一种舍入模式,可以调用其
setRoundingMode(RoundingMode roundingMode)
方法,该类提供各种形式的对数字输出格式化的方法,具体API可
参考相关文档,
关于这个类的用法,暂不做深究。)
对于问题2,最优的方法是转为String,但是当数值超过1千万时直接转换为字符串 会采用 “科学计数法” 。所以虽然能解决,但是
会显得比较麻烦。所以,暂时并没有什么特别好的方法。
一:初始化
Bigdecimal类提供的构造方法有很多,最常用的两个:
1. BigDecimal(double val)
2. BigDecimal(String str)
因为double类型的不精确性,所以利用方式一构造的Bigdecimal同样也是不精确的。
二:运算
BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的
方法。方法中的参数也必须是BigDecimal的对象。
public class BigDecimalUtil {
private BigDecimalUtil() {
}
public static BigDecimal add(double v1, double v2) {// v1 + v2
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2);
}
public static BigDecimal sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2);
}
public static BigDecimal mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2);
}
public static BigDecimal div(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
// 2 = 保留小数点后两位 ROUND_HALF_UP = 四舍五入
return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);// 应对除不尽的情况
}
}
除法divide()参数使用注意事项
使用除法函数在divide的时候要设置各种参数,要精确的小数位数和舍入模式,不然可能会出现报错
我们可以看到divide函数配置的参数如下
public BigDecimal divide(BigDecimal divisor 除数, int scale 精确小数位, RoundingMode roundingMode 舍入模式)
计算1÷3的结果(最后一种ROUND_UNNECESSARY在结果为无限小数的情况下会报错)
三:舍入模式
1、ROUND_UP
舍入远离零的舍入模式。
在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。
注意,此舍入模式始终不会减少计算值的大小。
2、ROUND_DOWN
接近零的舍入模式。
在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1,即截短)。
注意,此舍入模式始终不会增加计算值的大小。
3、ROUND_CEILING
接近正无穷大的舍入模式。
如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;
如果为负,则舍入行为与 ROUND_DOWN 相同。
注意,此舍入模式始终不会减少计算值。
4、ROUND_FLOOR
接近负无穷大的舍入模式。
如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同;
如果为负,则舍入行为与 ROUND_UP 相同。
注意,此舍入模式始终不会增加计算值。
5、ROUND_HALF_UP
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。
如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。
注意,这是我们大多数人在小学时就学过的舍入模式(四舍五入)。
6、ROUND_HALF_DOWN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。
如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同(五舍六入)。
7、ROUND_HALF_EVEN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同;
如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。
注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。
此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况。
如果前一位为奇数,则入位,否则舍去。
以下例子为保留小数点1位,那么这种舍入方式下的结果。
1.15>1.2 1.25>1.2
8、ROUND_UNNECESSARY
断言请求的操作具有精确的结果,因此不需要舍入。
如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException
此部分原文链接:https://blog.csdn.net/haiyinshushe/article/details/82721234
四:比较大小
1. BigDecial是immutable的,就像String一样,它的所有操作都会生成一个新的对象,不会对原对象做修改
2. 不要用equals方法来比较BigDecimal对象,因为它的equals方法会比较scale,如果scale不一样,它会返回false
3. 比较大小,应该使用这两个方法
public int compareTo(BigDecimal val) 比大小
public int signum() 确定正负