day1-后续2:BigDecimal的浮点数运算能保证精度的原理是什么?

不是所有的十进制数都能转化为有限位二进制数的。

1、任意十进制整数可以转化为有限位数的二进制整数。

如123=64+32+16+8+2+1,转化为二进制整数是1111011。

2、能分解为以(1/2)^n为单位的十进制小数,可以转化为有限位数的二进制小数。

如十进制数:13/16=0.8125,它可以是拆成:13/16=1/2+1/4+1/16,或者直接可以看作是13个1/16所组成。而1/2,1/4,1/16这些数都是符合(1/2)^n形式的数。

所以13/16转化为4位二进制小数:0.1101。

3、上述情形以外的十进制数都不能转化为有限位数的二进制数。

如十进制小数0.7,转化为二进制小数是:0.1011001100110......,循环节是0110。

当第三种情况的小数出现的时候,就会出现计算的精度误差,BigDecimal的原理很简单,就是将小数扩大N倍,转成整数后再进行计算,同时结合指数,得出没有精度损失的结果。

我们看一下BigDecimal构造原理

部分内容转载自:

[java基础原理] BigDecimal​www.cnblogs.com/ihzeng/p/10747803.html​编辑

package java.math;
 
public class BigDecimal {
    //值的绝对long型表示
    private final transient long intCompact;
    //值的小数点后的位数
    private final int scale;
 
    private final BigInteger intVal;
    //值的有效位数,不包含正负符号
    private transient int precision;
    private transient String stringCache;
     
    //加、减、乘、除、绝对值
    public BigDecimal add(BigDecimal augend) {}
    public BigDecimal subtract(BigDecimal subtrahend) {}
    public BigDecimal multiply(BigDecimal multiplicand) {}
    public BigDecimal divide(BigDecimal divisor) {}
    public BigDecimal abs() {}
}

2.对象简化示例

2.1 以long型的intCompact和scale来存储精确的值。

2.2 包含stringCache,因此创建BigDecimal对象时,优先转换成String类型,比如double转BigDecimal也是先double转成String,再String转成BigDecimal.

3.加减乘除的实现

加法:long类型 +

package bigdecimal;

import java.math.BigDecimal;

public class BigDecimalTest
{
	public static void main(String[] args)
	{
		final BigDecimal bigDecimal = new BigDecimal("1.13");
		final BigDecimal bigDecimal2 = new BigDecimal("1.15");
		
		bigDecimal.add(bigDecimal2);
		
		System.out.println(bigDecimal.doubleValue());
	}
}

    /**
     * Sentinel value for {@link #intCompact} indicating the
     * significand information is only available from {@code intVal}.
     */
    final static long INFLATED = Long.MIN_VALUE; 
   // Arithmetic Operations
    /**
     * Returns a {@code BigDecimal} whose value is {@code (this +
     * augend)}, and whose scale is {@code max(this.scale(),
     * augend.scale())}.
     *
     * @param  augend value to be added to this {@code BigDecimal}.
     * @return {@code this + augend}
     */
    public BigDecimal   add(BigDecimal augend) {
 
          long xs =this.intCompact; //整型数字表示的BigDecimal,例a的intCompact值为113
 
          long ys = augend.intCompact;//同上115
 
          //初始化 BigInteger的值,intVal为BigDecimal的一个BigInteger类型的属性
          BigInteger fst = (this.intCompact !=INFLATED) ?null :this.intVal;     
 
          BigInteger snd =(augend.intCompact !=INFLATED) ?null : augend.intVal;
 
          int rscale =this.scale;//小数位数 2
          long sdiff = (long)rscale - augend.scale;//小数位数之差 0
 
          if (sdiff != 0) {//取小数位数多的为结果的小数位数
 
              if (sdiff < 0) {
 
                 int raise =checkScale(-sdiff);
 
                 rscale =augend.scale;
 
                 if (xs ==INFLATED ||
 
                     (xs = longMultiplyPowerTen(xs,raise)) ==INFLATED)
 
                     fst =bigMultiplyPowerTen(raise);
 
                }else {
 
                   int raise =augend.checkScale(sdiff);
 
                   if (ys ==INFLATED ||(ys =longMultiplyPowerTen(ys,raise)) ==INFLATED)
 
                       snd = augend.bigMultiplyPowerTen(raise);
 
               }
 
          }
 
          if (xs !=INFLATED && ys !=INFLATED) {
 
              long sum = xs + ys;
 
              if ( (((sum ^ xs) &(sum ^ ys))) >= 0L)//判断有无溢出
 
                 return BigDecimal.valueOf(sum,rscale);//返回使用BigDecimal的静态工厂方法得到的BigDecimal实例
 
           }
 
           if (fst ==null)
 
               fst =BigInteger.valueOf(xs);//BigInteger的静态工厂方法
 
           if (snd ==null)
 
               snd =BigInteger.valueOf(ys);
 
           BigInteger sum =fst.add(snd);
 
           return (fst.signum == snd.signum) ?new BigDecimal(sum,INFLATED, rscale, 0) :
 
              new BigDecimal(sum,compactValFor(sum),rscale, 0);//返回通过其他构造方法得到的BigDecimal对象
 
       }

减法:转成加法,加负数

乘法: long类型 *, 多些进位超界判断

除法: long类型 /, 多些小数位数保留判断

4.BigDecimal能更精确表示带小数点的数值,因为采用了long intCompact和int scale来表示数值,而不是浮点型的科学计数法。

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值