商业计算不要使用浮点数
在前几期的每日一练题目中,有一道题目是,需要精确计算的时候一定要使用BigDecimal。在本科时候其实计算机原理就曾经学过计算机的浮点数表示,但是从小数学的惯性思维上,总觉得乘法就移一下小数点,不会出问题。在商业计算中遇到带小数点的“元”转换为“分”的时候,进行*100的操作总觉得double根本不会出问题,于是怒踩了一坑。
简单理解为什么会有精度问题
大家都知道计算机要用二进制表示,基本的十进制和二进制的换算应该都清楚。例如:
0.5(十进制) = 0.1(二进制),因为2^-1是0.5。
0.25(十进制) = 0.01(二进制),因为2^-2是0.25。
那0.2呢,怎么表示呢,表示不出来啊。那就只能找个最接近的近似值来代表。
所以0.2的二进制小数是0.0011001100110011.......,也就是(2^-3)+(2^-4)+(2^-7)+(2^-8)+.....
简单介绍IEEE754表示法
以Java中的double值0.2为例,double大小为32位。
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(0.2)));
运行以上代码可以看到double类型0.2在机器中的二进制表示为:11111111001001100110011001100110011001100110011001100110011010。
需要将其补齐64位,在前面加两个0:
0011111111001001100110011001100110011001100110011001100110011010。
按照IEEE754表示法,我们将其分为三段:
符号(64位):0,
阶码(63-53位):01111111100,
尾数(51-1位):001100110011001100110011001100110011001100110011010。
System.out.println(Integer.valueOf("01111111100",2));
运行以上代码可以看到阶码的十进制值为1020。当阶码不为0或2047时,需要减去1023这个偏移量,并且尾数前面隐藏了”1.”。因此,调整后的表示为:
符号:0,
阶码:-3(十进制),
尾数:1.1001100110011001100110011001100110011001100110011010。
最终,按照-1^符号 尾数 2^阶码的计算方式,0.2真正的二进制值如下所示,和真正的0.2明显是有偏差的:
0.0011001100110011001100110011001100110011001100110011010
Java中正确使用BigDecimal
在Java中遇到商业计算,可以方便的使用BigDecimal来解决精度问题。但是使用中也存在需要注意的地方。
double d = 0.2; System.out.println(new BigDecimal(d));
运行以上代码可以看到,java中表示的0.2的值为:
0.200000000000000011102230246251565404236316680908203125,是有偏差的。
但是虽然用了BigDecimal,为什么数据还是有问题?这是因为在使用BigDecimal时,不要使用double参数的构造方法,而是要使用String参数的构造方法,或是静态方法valueOf,才能够保证数据的准确性。
//两种正确的使用方式
System.out.println(new BigDecimal("0.2"));
System.out.println(BigDecimal.valueOf(0.2));
BigDecimal类创建的是对象,不能使用传统的+、-、*、/等算术运算符直接对其进行数学运算,而必须调用其对应的方法.方法的参数也必须是BigDecimal类型的对象.
构造BigDecimal 对象常用方法
方法一
BigDecimal BigDecimal(double d); //不允许使用
方法二
BigDecimal BigDecimal(String s); //常用,推荐使用
方法三
static BigDecimal valueOf(double d); //常用,推荐使用
注意:
1. double 参数的构造方法,不允许使用!!!!因为它不能精确的得到相应的值,值会变大;
2. String 构造方法是完全可预知的: 写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好等于预期的0.1; 因此,通常建议优先使用 String 构造方法;
3. 静态方法 valueOf(double val) 内部实现,仍是将 double 类型转为 String 类型; 这通常是将 double(或float)转化为 BigDecimal 的首选方法。
感谢大家阅读由大数据教程分享的“使用BigDecimal实现精确加减乘除运算”希望对大家有所帮助,更多精彩内容请关注大数据培训机构官网
免责声明:本文由小编转载自网络,旨在分享提供阅读,版权归原作者所有,如有侵权请联系我们进行删除