BigDecimal - 用于加、减、乘、除计算的工具类

开发过程中经常会需要进行一些数据的运算操作,比如加、减、乘、除,但是在计算过程中,到底使用哪种数据类型进行计算才是最合适的呢?


1、数据类型

1.1、 基础数据类型

  • int:只能进行整数的计算,应用范围单一。
  • float:单精度浮点型,可以进行小数类型的计算,但是容易丢失精度。
  • double:双精度浮点型,精确度比float高,消耗的内存也比float高,但是也容易丢失精度。
  • String:字符串类型,没法进行运算操作,但是恰恰要用到它和另外一种对象来进行数据的运算操作。
  • BigDecimal :Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。

1.2、BigDecimal 详细介绍

以下是关于BigDecimal 的官方文档介绍,以及自己的一些总结。涉及到BigDecimal 以及和它相关联的MathContextRoundingMode这两个类。

java.math.BigDecimal

  • javase官方文档的描述:

不可变的、任意精度的有符号十进制数。BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度 (scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负 scale 次幂。因此,BigDecimal 表示的数值是 (unscaledValue × 10-scale)。
BigDecimal 类提供以下操作:算术、标度操作、舍入、比较、哈希算法和格式转换。toString() 方法提供 BigDecimal 的规范表示形式。
BigDecimal 类使用户能完全控制舍入行为。如果未指定舍入模式,并且无法表示准确结果,则抛出一个异常;否则,通过向该操作提供适当的 MathContext 对象,可以对已选择的精度和舍入模式执行计算。在任何情况下,可以为舍入控制提供八种舍入模式。使用此类(例如,ROUND_HALF_UP)中的整数字段来表示舍入模式已过时;应改为使用 RoundingMode enum(例如,RoundingMode.HALF_UP)的枚举值。

java.math.MathContext

  • javase官方文档描述

该对象是封装上下文设置的不可变对象,它描述数字运算符的某些规则,例如由 BigDecimal 类实现的规则。
基本独立设置为:
1、precision:某个操作使用的数字个数;结果舍入到此精度
2、roundingMode:一个 RoundingMode 对象,该对象指定舍入使用的算法。

java.math.RoundingMode

  • javase官方文档描述

为可能丢弃精度的数值操作指定一种舍入行为。每种舍入模式都指示如何计算返回舍入结果位数的最低有效位。如果返回的位数比表示精确数值结果所需的位数少,则舍弃的位数称为舍弃部分,而不管这些位数对数值的作用如何。换句话说,假设是一个数值,舍弃部分的绝对值可能大于 1。

  • 这是一种枚举类型,以下是它的8种舍入方式的总结:
  1. UP

远离零方向舍入的舍入模式。
始终对非零舍弃部分前面的数字加 1。
注意,此舍入模式始终不会减少计算值的绝对值。

  1. DOWN

向零方向舍入的舍入模式。
从不对舍弃部分前面的数字加 1(即截尾)。
注意,此舍入模式始终不会增加计算值的绝对值。

  1. CEILING

向正无限大方向舍入的舍入模式。
如果结果为正,则舍入行为类似于 RoundingMode.UP;
如果结果为负,则舍入行为类似于 RoundingMode.DOWN。
注意,此舍入模式始终不会减少计算值。

  1. FLOOR

向负无限大方向舍入的舍入模式。
如果结果为正,则舍入行为类似于 RoundingMode.DOWN;
如果结果为负,则舍入行为类似于 RoundingMode.UP。
注意,此舍入模式始终不会增加计算值。

  1. HALF_UP

向最接近数字方向舍入的舍入模式。
如果与两个相邻数字的距离相等,则向上舍入。
如果被舍弃部分 >= 0.5,则舍入行为同 RoundingMode.UP;
否则舍入行为同 RoundingMode.DOWN。
注意,此舍入模式就是通常学校里讲的四舍五入

  1. HALF_DOWN

向最接近数字方向舍入的舍入模式。
如果与两个相邻数字的距离相等,则向下舍入。
如果被舍弃部分 > 0.5,则舍入行为同 RoundingMode.UP;
否则舍入行为同 RoundingMode.DOWN。

  1. HALF_EVEN

向最接近数字方向舍入的舍入模式。
如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
如果舍弃部分左边的数字为奇数,则舍入行为同 RoundingMode.HALF_UP;
如果为偶数,则舍入行为同 RoundingMode.HALF_DOWN。
注意,在重复进行一系列计算时,此舍入模式可以在统计上将累加错误减到最小。此舍入模式也称为“银行家舍入法”,主要在美国使用。此舍入模式类似于 Java 中对 float 和 double 算法使用的舍入策略。

  1. UNNECESSARY

用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入。
如果对生成精确结果的操作指定此舍入模式,则抛出 ArithmeticException。

  • 来个表格看一下吧(这是javase官方api的图表)根据给定的舍入模式将输入数字舍入为一位数的结果:
输入数字 UP DOWN CEILING FLOOR HALF_UP HALF_DOWN HALF_EVEN UNNECESSARY
5.5 6 5 6 5 6 5 6 抛出 ArithmeticException
2.5 3 2 3 2 3 2 2 抛出 ArithmeticException
1.6 2 1 2 1 2 2 2 抛出 ArithmeticException
1.1 2 1 2 1 1 1 1 抛出 ArithmeticException
1.0 1 1 1 1 1 1 1 1
-1.0 -1 -1 -1 -1 -1 -1 -1 -1
-1.1 -2 -1 -1 -2 -1 -1 -1 抛出 ArithmeticException
-1.6 -2 -1 -1 -2 -2 -2 -2 抛出 ArithmeticException
-2.5 -3 -2 -2 -3 -3 -2 -2 抛出 ArithmeticException
-5.5 -6 -5 -5 -6 -6 -5 -6 抛出 ArithmeticException

注意:以上8中枚举类型,在使用中需要特别注意,尤其是除法运算中的时候,需要制定枚举类型,切记,切记。

2、使用环境

在进行开发工作中,有时候会需要对一些数据进行加、减、乘、除的运算操作,这时候无论用int、float还是double进行计算,都会存在精度丢失的风险,这时候我们需要借助一个对象BigDecimal 来帮助我们进行计算。
BigDecimal 的构造方法有许多个,构造参数可以传int、float、double、String,但是经实际应用,使用构造参数为float或double类型的构造参数进行计算的时候,转换的BigDecimal 对象都会导致精度的丢失。通过实际验证,使用构造参数为String的构造方法可以避免精度丢失。
注意: 在以后需要进行加、减、乘、出计算的参数,都尽量用String来接收服务器返回的参数。

3、使用方式

以下是封装的用于进行计算的方法:

加法

  /**
     * 提供精确加法计算的add方法(例:1+2,1是被加数,2是加数)
     *     
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static String add(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.add(b2).toString();
    }

减法

 /**
     * 提供精确减法运算的sub方法(例:1-2,1是被减数,2是减数)
     *
     * @param value1 被减数
     * @param value2 减数
     * @return 两个参数的差
     */
    public static String sub(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.subtract(b2).toString();
    }

乘法

 /**
     * 提供精确乘法运算的mul方法(例:1*2,1是被乘数,2是乘数)
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static String mul(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.multiply(b2).toString();
    }

除法

 /**
     * 提供精确的除法运算方法div(例:1÷2,1是被除数,2是除数)
     *
     * @param value1 被除数
     * @param value2 除数
     * @param scale  精确范围
     * @return 两个参数的商
     * @throws IllegalAccessException
     */
    public static String div(String value1, String value2, int scale) throws IllegalAccessException {
        //如果精确范围小于0,抛出异常信息
        if (scale < 0) {
            throw new IllegalAccessException("精确度不能小于0");
        }
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        //指定枚举类型,可以指定RoundingMode的8种枚举类型之一,对这8种枚举类型如果有疑问,可以参考上面RoundingMode的总结和描述。
        //这里指定的DOWN,对应RoundingMode的ROUND_DOWN模式,会直接舍去精确度后面的数值。
        return b1.divide(b2, scale, RoundingMode.DOWN).toString();
    }

4、使用总结

用以上四个方法就可以应付开发过程中最基础的加、减、乘、除的运算了,但是在使用中要注意的情况:被减数和减数、被除数和除数的位置不要颠倒了,否则就会和预期的计算结果相差十万八千里了。

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

推荐阅读更多精彩内容