您的位置:首页 > 其它

使用BigDecimal实现精确加减乘除运算

2018-03-20 18:32 459 查看
商业计算不要使用浮点数
在前几期的每日一练题目中,有一道题目是,需要精确计算的时候一定要使用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.0011001100110011001100110011001100110011001100110011010Java中正确使用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 的首选方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: