不是所有的十进制数都能转化为有限位二进制数的。
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基础原理] BigDecimalwww.cnblogs.compackage 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来表示数值,而不是浮点型的科学计数法。