JAVA BigDecimal 类使用总计及闭坑指南

概念

BigDecimal是一个不可变的,任意精度的有符号十进制数

BigDecimal由任意精度的整数非标度值和32位的整数标度(scale)组成。如果为零或整数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以10的负scale次幂。因此,BigDecimal表示的数值是(unscaledValueX10‾scale)。

BigDecimal对象内通过BigInteger IntVal存储传递对象数字部分,通过int scale记录小数点位数,通过int precision记录有效位数(默认为0)。

BigDecimal的加减乘除就成了BigInteger与BigInteger之间的加减乘除,浮点数的计算也转化为整形的计算,可以大大提供性能,并且通过BigInteger可以保存大数字,从而实现真正十大进制的计算,在整个计算过程中,还涉及scale的判断和precision判断从而确定最终输出结果。

            在Java中,由CPU原生提供的整型最大范围是64位long型整数。使用long型整数可以通过CPU指令进行计算,速度会非常快。果使用的整数范围超过了long型,BigInteger就是用来表示任意大小的整数,BigInteger内部用一个int[]数组来模拟一个非常大的数。

类中属性

scale:有多少位小数(即小数点后有多少位)

precision:一共有多少位数字,在确定了precision后就会要求结合Rounding Mode做一些舍入方面的操作

intVal和scale,分别表示BigDecimal的无标度值和标度,BigDecimal可以表示为一个任意精度的无标度值和一个32位整型的标度

intCompact:字符串去掉小数点后,转为long的值,如果intVal在compact的过程中超过了Long.MAX_VAULE则将intCompact记为Long.MIN_VALUE

intVal:当传的字符串长度大于等于18时才使用BigInteger表示数字

stringCache:在toString方法的时候用到

BigDecimal 继承了Number并实现了Comparable接口。

继承了Number,将会提供将表示的数值转换为byteValue()、shortValue()、intValue()、longValue()、floatValue()、doubleValue()的方法

实现了Comparable接口,可以使用compareTo()方法来进行比较

问题:

使用newBigDecimal(double)的方式计算精确数值,会有精度缺失的问题,使用BigDecimal.valueOf(double)的方式就不会存在此问题。

具体原因:

float与double类型主要是为了科学计算和工程计算而设计。为了在广泛的数值范围上提供较为精确的快速近和计算而精心设计的,并没有完全精确的结果,所以不应该被用于精确结果的场合。

        new BigDecimal()源码


BigDecimal.valueOf()源码


不同的地方在于valueOf()方法在对double类型转换的时候,做了一次转换为字符串的操作,从而避免了不准确的问题;但是如果是float类型,那么此方法又会出现不准确的问题。

参数类型为int,long,字符串时,这两种方式没有任何区别。

正确的做法是自己构造BigDecimal对象时,使用String.valueOf()将参数显示转换为字符串。官方注释也有说明此问题,使用new BigDecimal(double val)方法得到的结果是不可预知的,推荐使用入参类型为String的构造函数来进行浮点数的精确计算。

当入参类型为float时,使用valueOf()方法以及new BigDecimal()构造方法会出现浮点运算精度不一致的问题。使用String.valueOf(String val)方法,运算结果正确。

valueOf()方法对参数进行一次double的转换,9.9f会被强制转换为double类型,所以会出现精度问题。

BigDecimal比较问题(equals与compareTo)

问题:当入参为string类型时,会出现此问题。如果入参类型不为string类型,并不会出现此问题,但是会有精度不准确的影响。

使用BigDecimal(“0.00”).equals(BigDecimal.ZERO)与BigDecimal(0.00).equals(BigDecimal.ZERO)区别

解决方案:可以使用compareTo(BigDecimal.ZERO)==0,来判断是否等于0

结论:对于BigDecimal的大小比较,使用equals方法的话不仅会比较值的大小,还会比较两个对象的精确度,而compareTo方法数值相同精度不同也会被视认为相等。

compareTo()方法中的注释有所体现。


使用方式案例

常用构造函数

BigDecimal(int):创建一个具有参数所指定整数值的对象

BigDecimal(double):创建一个具有参数所指定双精度值的对象

BigDecimal(long):创建一个具有参数所指定长整数值的对象

Bigdecimal(String):创建一个具有参数所指定以字符串表示的数值的对象

常用方法

add(BigDecimal):BigDecimal对象中的值相加,返回BigDecimal对象

substract(BigDecimal):BigDecimal对象中的值相减,返回BigDecimal对象

multiply(BigDecimal):BigDecimal对象中的值相乘,返回BigDecimal对象

divide(BigDecimal):相除

doubleValue():转双精度

longValue():转长整型

intValue():转整型

floatValue():转单精度

toString():将BigDecimal对象中的值转换成字符串

BigDecimal大小比较

BigDecimal比较大小一般用的是compareTo方法

BigDecimal big=new BigDecimal(5.2);

int i = big.compareTo(new BigDecimal(2.2));

System.out.println(i);


BigDecimal.ROUND_DOWN

ROUND_DOWN:直接省略掉指定位数后的内容

BigDecimal b=new    BigDecimal("2.2345").setScale(2,Bigdecimal.ROUND_DOWN); // 2.23


BigDecimal.ROUND_UP

// 直接对指定位数后的内容做进一位处理

BigDecimal b=new BigDecimal("2.2355").setScale(2,Bigdecimal.ROUND_DOWN);// 2.24


BigDecimal.ROUND_CEILING

// 正数使用ROUND_UP规则,负数使用ROUND_DOWN规则

BigDecimal b=new BigDecimal("2,125446").setScale(2,BigDecimal.ROUND_CEILING);// 2.13

BigDecimal b=new BigDecimal("-2.1253456").setScale(2,BigDecimal.ROUND_CEILING);// -2.12


BigDecimal.ROUND_FLOOR

// 正数省略内容,负数向下进一位

BigDecimal b=new BigDecimal("2.125456").setScale(2,BigDecimal.ROUND_FLOOR);// 2.12

BigDecimal b=new BigDecimal("-2.125456").setScale(2,BigDecimal.ROUND_FLOOR);// -2.13


BigDecimal.ROUND_HALF_UP BigDecimal.ROUND_HALF_DOWN 四舍五入

BigDecimal b=new BigDecimal("2.125456").setScale(2,BigDecimal.ROUND_HALF_UP);// 2.13

BigDecimal b=new BigDecimal("-2.125456").setScale(2,BigDecimal.ROUND_HALF_DOWN);// -2.13


BigDecimal.ROUND_HALF_EVEN

指定小数位的前一位,如果是奇数则四舍五入后进位,如果是偶数则舍弃指定小数位后面内容

BigDecimal bigDecimalA = new BigDecimal("2.113").setScale(2, BigDecimal.ROUND_HALF_EVEN);

// 2.11  虽然是奇数,但是3<5,不会进位

BigDecimal bigDecimalB = new BigDecimal("2.115").setScale(2, BigDecimal.ROUND_HALF_EVEN);

// 2.12  因为是奇数且符合"五入",则进位

此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况。

如果前一位为奇数,则入位,否则舍去。


ROUND_UNNECESSARY

断言请求的操作具有精确的结果,因此不需要舍入

如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException


其他注意问题

在使用divide方法进行除法时当不整除,出现无限循环小数时,就会抛异常。

解决方法:divide方法设置精确的小数点,divide(xxxxx,2)

小总结

不要随便使用BigDecimal,一般精度的计算没必要使用BigDecimal。因为BigDecimal的精度比double和float性能差。在处理庞大复杂的计算尤为明显。

尽量使用参数类型为String的构造函数

BigDecimal都是不可变的(immutable)的,在进行每一次四则运算时,都会产生一个新的对象,所以在做加减乘除运算时要记得保存操作后的值

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容

  • 一、引言 float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广...
    AC编程阅读 228评论 0 1
  • | java.math |--java.math.BigDecimal |--java.math.BigInteg...
    flyrae阅读 2,779评论 0 2
  • java.lang.Math(final类) Java 语言是彻底地面向对象语言,哪怕是进行数学运算也封装到一个类...
    acc8226阅读 192评论 0 0
  • 文章首次发布于https://www.elichen.club 小数计算丢失精度问题 在计算机中,所有文件都是以二...
    EliCHEN阅读 1,382评论 1 0
  • 一、BigDecimal 的介绍 BigDecimal是Java在java.math包中提供的API类,用来对...
    south_zn阅读 5,705评论 0 0