1. 为什么用 BigDecimal
?
因为 double float的计算很不靠谱 莫名其妙的会出现许多数字
示例如下:
public class DecimalTest {
public static void main(String[] args) {
double d1 = 3.12d;
double d2 = 2.52d;
System.out.println(d1+d2);
//打印结果:5.640000000000001
}
}
这不是坑爹嘛 谁搞出来这种东西的? 我们来看一下说明 effective java 第209页
是怎么说的
float 和 double 类型主要是为了科学计算和工程计算而设计的,它们执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。
然而,它们并没有提供完全精确的结果,所以不应该被用于需要精确结果的场合 float double 类型尤其不适合用于货币计算 ,因为要让一个 float,double 精确地表示 0.1 (或者 的任何其他负数次方值 )是不可能的。
所以我们只能使用 BigDecimal
了,虽然我觉得用起来很不方便,当然也可以选择int类型或者long类型来处理,我们后面再说。
2. 构造函数的选择
BigDecimal
提供了多种构造函数,但我们最常用的还是传入字符串,而不要传double 否则会出问题的,案例如下
public class DecimalTest {
public static void main(String[] args) {
//1.直接传入字符串
BigDecimal b1 = new BigDecimal("7.12312");
//传入double类型
BigDecimal b2 = new BigDecimal(7.12312d);
System.out.println(b1);// 7.12312
System.out.println(b2);//7.123120000000000118234311230480670928955078125
}
}
屁股后面出现了多位小数,再和其他数字进行运算,结果还会对吗?
总的来说,无脑传字符串即可
3.加减乘除的使用
BigDecimal
的运算方法当然必不可少,简单介绍下,会用即可
public class DecimalTest {
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("3.5");
BigDecimal b2 = new BigDecimal("0.5");
//加法运算
System.out.println(b1.add(b2));//4.0
//减法运算
System.out.println(b1.subtract(b2));//3.0
//乘法运算
System.out.println(b1.multiply(b2));//1.75
//除法法运算
System.out.println(b1.divide(b2));//7
//除法运算的特殊情况
System.out.println(b1.divide(new BigDecimal("1.7")));
}
}
加减乘都是没什么问题的 但是有一点需要注意
重点: 除法运算遇到除不尽的情况,将会抛出异常,如下所示
翻译下来就是:非终止十进制扩展;没有精确的可表示的十进制结果
那么如何解决呢,我们需要在使用除法的时候,在方法后面传参数,定义保留几位小数即可
4. 保留小数
就跟下面这样
public class DecimalTest {
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("3.5");
//除法运算的特殊情况
BigDecimal b3 = b1.divide(new BigDecimal("1.7"),5 RoundingMode.HALF_UP);
System.out.println(b3);//2.05882
}
}
我们来研究下 divide
方法的重载,主要就两个
public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode) {
return this.divide(divisor, scale, roundingMode.oldMode);//1
}
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {//2
}
第一个方法:第一个参数 BigDecimal类型,第二个参数,按什么方式保留
其实第一个方法真正调用的还是三个参数的重载方法,细心的小伙伴可能发现了,在 //1处的scale是什么?从哪里来的呢?
其实scale就是小数点后面的位数啦
注意,他调用的是 this.divide
,也就是说,按被除数的小数点后面位数进行保留即可,不信?来看下例子
public class DecimalTest {
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("3.5");
//除法运算的特殊情况
BigDecimal b3 = b1.divide(new BigDecimal("1.7"), RoundingMode.HALF_UP);
System.out.println(b3);//2.1
}
}
输出的是2.1
再把b1的小数扩大
public class DecimalTest {
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("3.5555555");
//除法运算的特殊情况
BigDecimal b3 = b1.divide(new BigDecimal("1.7"), RoundingMode.HALF_UP);
System.out.println(b3);//2.0915032
}
}
可以看到 确实如我们所料
接下来再介绍下 RoundingMode
类,它定义了以什么方式保留小数 如四舍五入,向上取整,向下取整等
5. RoundingMode类
看下jdk文档的介绍 这玩意就是个枚举类
最常用的就是HALF_UP了 我们可以使用setScale()
方法来指定保留的位数和模式
public class DecimalTest {
public static void main(String[] args) {
BigDecimal b2 = new BigDecimal("3.53");
System.out.println(b2.setScale(0, RoundingMode.HALF_UP));//4
}
}
详细的介绍参考下这两个链接吧 因为其他的都不是很常用
6.其他方法
说一下常用的其他方法
//1 BigDecimal转化为double类型
public double doubleValue();
//2.取余方法
public BigDecimal remainder(BigDecimal divisor);
//3.比较方法
*/
public int compareTo(BigDecimal val);
1.BigDecimal转化为double类型 再强调如果要double转化为Bigdecimal
务必先将double类型转化为字符串 而后使用字符串构造方法!!! 务必先将double类型转化为字符串 而后使用字符串构造方法!!! 务必先将double类型转化为字符串 而后使用字符串构造方法!!!2. 取余方法 保留的位数由被除数决定或者说调用方法的对象小数有几位就保留几位
3.一般对于BigDecimal类型 我们不能转化为double再比较 而需要使用这个方法
如 a.compartTo(b)
a>b 返回1
a=b 返回0
a<b 返回-1
最后来个 effective java 的总结
总而言之,对于任何需要精确答案的计算任务,请不要使用 float 或者 double。
如果你想让系统来处理十进制小数点,并且不介意因为不使用基本类型而带来的不便,就请使用BigDecmal,使用 BigDecimal 还有 些额外的好处,它允许你完全控制舍入,每当一个操作涉及舍入的时候,你都可以从 种舍入模式中选择其一。
如果你正通过合法强制的舍入行为进行商务计算,使用 BigDecimal 是非常方便的,如果性能非常关键,并且你又不介意自己处理十进制小数点,而且所涉及的数值又不太大,就可以使用 int 或者 long类型。
如果果数值范围没有超过 9 位十进制数字,就可以使用int ;如果不超过 18 位数字,就可以使long,如果数值可能超过 18 位数字,就必须使用 BigDecimal 。