文档章节

BigDecimal浅析

wjk_snail
 wjk_snail
发布于 2016/01/06 18:02
字数 1006
阅读 242
收藏 7

为什么使用BigDecimal

首先看一个例子:
public class DoubleTest {
public static void main(String[] args) {
    System.out.println(0.1 + 0.2);
    }
}
输出的结果:0.30000000000000004(我们的预期是0.3)
其实float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。所有就出现了BigDecimal。

jdk文档BigDecimal的描述:
不可变的、任意精度的有符号十进制数。BigDecimal 由任意精度的整数非标度值和32位的整数标度(scale)组成。BigDecimal 表示的数值是 (unscaledValue × 10-scale)。

BigDecial构造方法

下面是常用的四种构造方法:
BigDecimal(int)       
BigDecimal(double) 
BigDecimal(long)   
BigDecimal(String)

public static void main(String[] args) {
    double a = 123.11;
    BigDecimal bigDecimal = new BigDecimal(a);
    System.out.println(bigDecimal);
}
输出结果:123.1099999999999994315658113919198513031005859375
public static void main(String[] args) {
    String a = "123.11";
    BigDecimal bigDecimal = new BigDecimal(a);
    System.out.println(bigDecimal);
}
输出结果:123.11
public static void main(String[] args) {
    double a = 123.11;
    System.out.println(BigDecimal.valueOf(a));
}
输出结果:123.11

BigDecimal(double)这个构造方法为什么会输出不是我们预期的值:
查看源码的注释得知: new BigDecimal(0.1)所创建的BigDecimal实际上等于 0.1000000000000000055511151231257827021181583404541015625,这是因为0.1无法准确地表示为 double(或者不能表示为任何有限长度的二进制小数),所以建议使用BigDecimal(String)构造方法。

valueOf其实也调用BigDecimal(String)构造方法
public static BigDecimal valueOf(double val) {
    // Reminder: a zero double returns '0.0', so we cannot fastpath
    // to use the constant ZERO.  This might be important enough to
    // justify a factory approach, a cache, or a few private
    // constants, later.
    return new BigDecimal(Double.toString(val));
}

BigDecimal基本运算

add(BigDecimal)        BigDecimal对象中的值相加
subtract(BigDecimal) BigDecimal对象中的值相减 
multiply(BigDecimal)  BigDecimal对象中的值相乘 
divide(BigDecimal)     BigDecimal对象中的值相除
注意:+-* /运算返回的是新的BigDecimal对象
比较大小: compareTo()  
-1、0、1,即左边比右边数大,返回1,相等返回0,比右边小返回-1。注意不能使用equals方法来比较大小。

//add(BigDecimal)源码
public BigDecimal add(BigDecimal augend) {
    if (this.intCompact != INFLATED) {
        if ((augend.intCompact != INFLATED)) {
            return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
        } else {
            return add(this.intCompact, this.scale, augend.intVal, augend.scale);
        }
    } else {
        if ((augend.intCompact != INFLATED)) {
            return add(augend.intCompact, augend.scale, this.intVal, this.scale);
        } else {
            return add(this.intVal, this.scale, augend.intVal, augend.scale);
        }
    }
}
//add
private static BigDecimal add(final long xs, int scale1, BigInteger snd, int scale2) {
    int rscale = scale1;
    long sdiff = (long)rscale - scale2;
    boolean sameSigns =  (Long.signum(xs) == snd.signum);
    BigInteger sum;
    if (sdiff < 0) {
        int raise = checkScale(xs,-sdiff);
        rscale = scale2;
        long scaledX = longMultiplyPowerTen(xs, raise);
        if (scaledX == INFLATED) {
            sum = snd.add(bigMultiplyPowerTen(xs,raise));
        } else {
            sum = snd.add(scaledX);
        }
    } else { //if (sdiff > 0) {
        int raise = checkScale(snd,sdiff);
        snd = bigMultiplyPowerTen(snd,raise);
        sum = snd.add(xs);
    }
    return (sameSigns) ?
        //新对象
        new BigDecimal(sum, INFLATED, rscale, 0) :
        //valueOf()返回的也是新对象
        valueOf(sum, rscale, 0);
}

BigDecimal格式化

如果我们要将String转换为BigDecimal,然后保留两位小数。

DecimalFormat format = new DecimalFormat("0.00");
System.out.println(format.format(new BigDecimal(str)));

//如果四舍五入呢
String str = "123.4444";
System.out.println(new BigDecimal(str).setScale(2,BigDecimal.ROUND_HALF_UP));

参数
直接删除多余的小数位,如2.35会变成2.3 setScale(1,BigDecimal.ROUND_DOWN) 

进位处理,2.35变成2.4  setScale(1,BigDecimal.ROUND_UP) 

四舍五入,2.35变成2.4  setScale(1,BigDecimal.ROUND_HALF_UP) 

四舍五入,2.35变成2.3,如果是5则向下舍setScaler(1,BigDecimal.ROUND_HALF_DOWN)

BigDecimal使用时注意点

(1)尽量使用参数类型为String的构造函数。

(2) BigDecimal都是不可变的,每次计算会产生新的对象,所以+-*/后保存值,如:a.dd(b)要写成a = a.add(b)。

BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度 (scale) 组成理解

例如:-314和 3.1415
表示为:-314 × 10-0 和13412 × 10-4
这里用(非标度值 和 标度)表示分别为:[-314, 0]和[13412, 4]

BigDecimal amount = new BigDecimal("314");
System.out.println(amount.signum());//正负
System.out.println(amount.scale()); //标度
System.out.println(amount.stripTrailingZeros());
System.out.println(amount.stripTrailingZeros().scale());//去零后的标度(注意是末尾的0)
//结果
1
0
314
0

BigDecimal amount = new BigDecimal("3.1415");
System.out.println(amount.signum());//正负
System.out.println(amount.scale()); //标度
System.out.println(amount.stripTrailingZeros());
System.out.println(amount.stripTrailingZeros().scale());//去零后的标度
//结果
1
4
3.1415
4
//大家尝试理解判断是否是整数这段代码
private boolean isIntegerValue(BigDecimal bd) {  
  return bd.signum() == 0 || bd.scale() <= 0 || bd.stripTrailingZeros().scale() <= 0;  
}





© 著作权归作者所有

wjk_snail
粉丝 8
博文 70
码字总数 50704
作品 0
东城
私信 提问
BigDecimal类的加减乘除

BigDecimal类型(+ - * /)所用的属性 11.10 BigDecimal类 对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDecimal类,而且使用BigDe...

zkool
2012/12/18
140
0
BigDecimal类的加/减/乘/除

对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDecimal类,而且使用BigDecimal类也可以进行大数的操作。BigDecimal类的常用方法如...

easonwang
2012/01/02
739
0
转:BigDecimal 使用方法详解

转贴:http://zhangyinhu8680.iteye.com/blog/1536397 BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度 (scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该...

克温s
2016/05/13
137
0
Java中BigDecimal的加减乘除和比较大小

CPU表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。java的float只能用来进行科学计算或工程计算,在大多数的商业计算中...

Ethel_oo
2017/09/27
29
0
BigDecimal用法详解

一、简介 Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算...

肖歌
2016/02/25
71
0

没有更多内容

加载失败,请刷新页面

加载更多

OpenStack 简介和几种安装方式总结

OpenStack :是一个由NASA和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。项目目标是提供实施简单、可大规模扩展、丰富、标准统一的云计算管理平台。OpenSta...

小海bug
昨天
6
0
DDD(五)

1、引言 之前学习了解了DDD中实体这一概念,那么接下来需要了解的就是值对象、唯一标识。值对象,值就是数字1、2、3,字符串“1”,“2”,“3”,值时对象的特征,对象是一个事物的具体描述...

MrYuZixian
昨天
6
0
数据库中间件MyCat

什么是MyCat? 查看官网的介绍是这样说的 一个彻底开源的,面向企业应用开发的大数据库集群 支持事务、ACID、可以替代MySQL的加强版数据库 一个可以视为MySQL集群的企业级数据库,用来替代昂贵...

沉浮_
昨天
6
0
解决Mac下VSCode打开zsh乱码

1.乱码问题 iTerm2终端使用Zsh,并且配置Zsh主题,该主题主题需要安装字体来支持箭头效果,在iTerm2中设置这个字体,但是VSCode里这个箭头还是显示乱码。 iTerm2展示如下: VSCode展示如下: 2...

HelloDeveloper
昨天
7
0
常用物流快递单号查询接口种类及对接方法

目前快递查询接口有两种方式可以对接,一是和顺丰、圆通、中通、天天、韵达、德邦这些快递公司一一对接接口,二是和快递鸟这样第三方集成接口一次性对接多家常用快递。第一种耗费时间长,但是...

程序的小猿
昨天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部