Java之BigDecimal详解

Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以开发中,如果我们需要精确计算的结果,则必须使用BigDecimal类来操作。BigDecimal所创建的是对象,故我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。

1.1、常用构造函数

  1. BigDecimal(int)

    创建一个具有参数所指定整数值的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public static void main(String[] args) {
    BigDecimal a = new BigDecimal(1);
    System.out.println("BigDecimal的参数为int类型:" + a );
    System.out.println("===============================");
    }
    /*
    输出结果:
    BigDecimal的参数为int类型:1
    */
  2. BigDecimal(double)

    创建一个具有参数所指定双精度值的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public static void main(String[] args) {
    BigDecimal a = new BigDecimal(1.1);
    BigDecimal b = new BigDecimal(1.11);
    BigDecimal c = new BigDecimal(1.111);
    System.out.println("BigDecimal的参数为double类型:" + a );
    System.out.println("===============================");
    System.out.println("BigDecimal的参数为double类型:" + b );
    System.out.println("===============================");
    System.out.println("BigDecimal的参数为double类型:" + c );
    System.out.println("===============================");
    }
    /*
    输出结果:
    BigDecimal的参数为double类型:1.100000000000000088817841970012523233890533447265625
    ===============================
    BigDecimal的参数为double类型:1.1100000000000000976996261670137755572795867919921875
    ===============================
    BigDecimal的参数为double类型:1.1109999999999999875655021241982467472553253173828125
    */
  3. BigDecimal(long)

    创建一个具有参数所指定长整数值的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public static void main(String[] args) {
    BigDecimal a = new BigDecimal(111111111111111111L);
    System.out.println("BigDecimal的参数为long类型:" + a );
    System.out.println("===============================");
    }
    /*
    输出结果:
    BigDecimal的参数为long类型:111111111111111111
    */
  4. BigDecimal(String) 常用

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public static void main(String[] args) {
    BigDecimal a = new BigDecimal("1.000010111111111111111111111111111");
    System.out.println("BigDecimal的参数为String类型:" + a );
    System.out.println("===============================");
    }
    /*
    输出结果:
    BigDecimal的参数为String类型:1.000010111111111111111111111111111
    */

BigDecimal(double) 与 BigDecimal(String)

先看例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
BigDecimal a = new BigDecimal(1.1);
BigDecimal b = new BigDecimal("1.1");
System.out.println("BigDecimal的参数为double类型:" + a );
System.out.println("===============================");
System.out.println("BigDecimal的参数为String类型:" + b );
}
/*
输出结果:
BigDecimal的参数为double类型:1.100000000000000088817841970012523233890533447265625
===============================
BigDecimal的参数为String类型:1.1
*/

原因:

1)参数类型为double的构造方法的结果有一定的不可预知性。有人可能认为在Java中写入newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于0.1000000000000000055511151231257827021181583404541015625。这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。

2)String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言, 通常建议优先使用String构造方法。

3)当double必须用作BigDecimal的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用Double.toString(double)方法,然后使用BigDecimal(String)构造方法,将double转换为String。要获取该结果,请使用static valueOf(double)方法。

1.2、BigDecimal常用方法详解

  1. 加减乘除

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public static void main(String[] args) {
    BigDecimal a = new BigDecimal("3.1");
    BigDecimal b = new BigDecimal("6.6");
    //加法
    BigDecimal addResult = a.add(b);
    //减法
    BigDecimal subResult = a.subtract(b);
    //乘法
    BigDecimal mulResult = a.multiply(b);
    //除法
    BigDecimal divResult = a.divide(b,2);
    System.out.println("加法运算结果-------->" + addResult);
    System.out.println("=================================");
    System.out.println("减法运算结果-------->" + subResult);
    System.out.println("=================================");
    System.out.println("乘法运算结果-------->" + mulResult);
    System.out.println("=================================");
    System.out.println("除法运算结果-------->" + divResult);
    System.out.println("=================================");
    }
    /*
    输出结果:
    加法运算结果-------->9.7
    =================================
    减法运算结果-------->-3.5
    =================================
    乘法运算结果-------->20.46
    =================================
    除法运算结果-------->0.5
    =================================
    */

    注意:在做除法运算时,可能会出现除不尽的情况,这个时候会报异常!

    1
    2
    3
    Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
    at java.math.BigDecimal.divide(BigDecimal.java:1693)
    at org.sang.service.ArticleService.main(ArticleService.java:166)

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

  2. 比较大小

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public static void main(String[] args) {
    BigDecimal a = new BigDecimal("3.1");
    BigDecimal b = new BigDecimal("6.6");
    BigDecimal c = new BigDecimal("5.5");
    BigDecimal d = new BigDecimal("5.5");

    //比较大小
    int result1 = a.compareTo(b);
    int result2 = b.compareTo(c);
    int result3 = c.compareTo(c);
    System.out.println("小于---------->" + result1);
    System.out.println("大于---------->" + result2);
    System.out.println("等于---------->" + result3);
    }
    /*
    输出结果:
    小于---------->-1
    大于---------->1
    等于---------->0
    */

    在开发中进行比较是,如果是 小于 那个结果小于0;如果是 等于 那么结果等于0;如果是 大于 那么结果是大于0.

1.3、BigDecimal格式化

  1. BigDecimal格式化保留2为小数,不足则补0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static String formatToNumber(BigDecimal obj) {
    DecimalFormat df = new DecimalFormat("#.00");
    if(obj.compareTo(BigDecimal.ZERO)==0) {
    return "0.00";
    }else if(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(new BigDecimal(1))<0){
    return "0"+df.format(obj).toString();
    }else {
    return df.format(obj).toString();
    }
    }

1.4、BigDecimal总结

  1. 总结

    • 在需要精确的小数计算时再使用BigDecimal,BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。
    • 尽量使用参数类型为String的构造函数。
    • BigDecimal都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。
  2. 工具类推荐

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    package com.vivo.ars.util;
    import java.math.BigDecimal;

    /**
    * 用于高精确处理常用的数学运算
    */
    public class ArithmeticUtils {
    //默认除法运算精度
    private static final int DEF_DIV_SCALE = 10;

    /**
    * 提供精确的加法运算
    *
    * @param v1 被加数
    * @param v2 加数
    * @return 两个参数的和
    */

    public static double add(double v1, double v2) {
    BigDecimal b1 = new BigDecimal(Double.toString(v1));
    BigDecimal b2 = new BigDecimal(Double.toString(v2));
    return b1.add(b2).doubleValue();
    }

    /**
    * 提供精确的加法运算
    *
    * @param v1 被加数
    * @param v2 加数
    * @return 两个参数的和
    */
    public static BigDecimal add(String v1, String v2) {
    BigDecimal b1 = new BigDecimal(v1);
    BigDecimal b2 = new BigDecimal(v2);
    return b1.add(b2);
    }

    /**
    * 提供精确的加法运算
    *
    * @param v1 被加数
    * @param v2 加数
    * @param scale 保留scale 位小数
    * @return 两个参数的和
    */
    public static String add(String v1, String v2, int scale) {
    if (scale < 0) {
    throw new IllegalArgumentException(
    "The scale must be a positive integer or zero");
    }
    BigDecimal b1 = new BigDecimal(v1);
    BigDecimal b2 = new BigDecimal(v2);
    return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
    * 提供精确的减法运算
    *
    * @param v1 被减数
    * @param v2 减数
    * @return 两个参数的差
    */
    public static double sub(double v1, double v2) {
    BigDecimal b1 = new BigDecimal(Double.toString(v1));
    BigDecimal b2 = new BigDecimal(Double.toString(v2));
    return b1.subtract(b2).doubleValue();
    }

    /**
    * 提供精确的减法运算。
    *
    * @param v1 被减数
    * @param v2 减数
    * @return 两个参数的差
    */
    public static BigDecimal sub(String v1, String v2) {
    BigDecimal b1 = new BigDecimal(v1);
    BigDecimal b2 = new BigDecimal(v2);
    return b1.subtract(b2);
    }

    /**
    * 提供精确的减法运算
    *
    * @param v1 被减数
    * @param v2 减数
    * @param scale 保留scale 位小数
    * @return 两个参数的差
    */
    public static String sub(String v1, String v2, int scale) {
    if (scale < 0) {
    throw new IllegalArgumentException(
    "The scale must be a positive integer or zero");
    }
    BigDecimal b1 = new BigDecimal(v1);
    BigDecimal b2 = new BigDecimal(v2);
    return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
    * 提供精确的乘法运算
    *
    * @param v1 被乘数
    * @param v2 乘数
    * @return 两个参数的积
    */
    public static double mul(double v1, double v2) {
    BigDecimal b1 = new BigDecimal(Double.toString(v1));
    BigDecimal b2 = new BigDecimal(Double.toString(v2));
    return b1.multiply(b2).doubleValue();
    }

    /**
    * 提供精确的乘法运算
    *
    * @param v1 被乘数
    * @param v2 乘数
    * @return 两个参数的积
    */
    public static BigDecimal mul(String v1, String v2) {
    BigDecimal b1 = new BigDecimal(v1);
    BigDecimal b2 = new BigDecimal(v2);
    return b1.multiply(b2);
    }

    /**
    * 提供精确的乘法运算
    *
    * @param v1 被乘数
    * @param v2 乘数
    * @param scale 保留scale 位小数
    * @return 两个参数的积
    */
    public static double mul(double v1, double v2, int scale) {
    BigDecimal b1 = new BigDecimal(Double.toString(v1));
    BigDecimal b2 = new BigDecimal(Double.toString(v2));
    return round(b1.multiply(b2).doubleValue(), scale);
    }

    /**
    * 提供精确的乘法运算
    *
    * @param v1 被乘数
    * @param v2 乘数
    * @param scale 保留scale 位小数
    * @return 两个参数的积
    */
    public static String mul(String v1, String v2, int scale) {
    if (scale < 0) {
    throw new IllegalArgumentException(
    "The scale must be a positive integer or zero");
    }
    BigDecimal b1 = new BigDecimal(v1);
    BigDecimal b2 = new BigDecimal(v2);
    return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
    * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
    * 小数点以后10位,以后的数字四舍五入
    *
    * @param v1 被除数
    * @param v2 除数
    * @return 两个参数的商
    */

    public static double div(double v1, double v2) {
    return div(v1, v2, DEF_DIV_SCALE);
    }

    /**
    * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
    * 定精度,以后的数字四舍五入
    *
    * @param v1 被除数
    * @param v2 除数
    * @param scale 表示表示需要精确到小数点以后几位。
    * @return 两个参数的商
    */
    public static double div(double v1, double v2, int scale) {
    if (scale < 0) {
    throw new IllegalArgumentException("The scale must be a positive integer or zero");
    }
    BigDecimal b1 = new BigDecimal(Double.toString(v1));
    BigDecimal b2 = new BigDecimal(Double.toString(v2));
    return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
    * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
    * 定精度,以后的数字四舍五入
    *
    * @param v1 被除数
    * @param v2 除数
    * @param scale 表示需要精确到小数点以后几位
    * @return 两个参数的商
    */
    public static String div(String v1, String v2, int scale) {
    if (scale < 0) {
    throw new IllegalArgumentException("The scale must be a positive integer or zero");
    }
    BigDecimal b1 = new BigDecimal(v1);
    BigDecimal b2 = new BigDecimal(v1);
    return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
    * 提供精确的小数位四舍五入处理
    *
    * @param v 需要四舍五入的数字
    * @param scale 小数点后保留几位
    * @return 四舍五入后的结果
    */
    public static double round(double v, int scale) {
    if (scale < 0) {
    throw new IllegalArgumentException("The scale must be a positive integer or zero");
    }
    BigDecimal b = new BigDecimal(Double.toString(v));
    return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
    * 提供精确的小数位四舍五入处理
    *
    * @param v 需要四舍五入的数字
    * @param scale 小数点后保留几位
    * @return 四舍五入后的结果
    */
    public static String round(String v, int scale) {
    if (scale < 0) {
    throw new IllegalArgumentException(
    "The scale must be a positive integer or zero");
    }
    BigDecimal b = new BigDecimal(v);
    return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
    * 取余数
    *
    * @param v1 被除数
    * @param v2 除数
    * @param scale 小数点后保留几位
    * @return 余数
    */
    public static String remainder(String v1, String v2, int scale) {
    if (scale < 0) {
    throw new IllegalArgumentException(
    "The scale must be a positive integer or zero");
    }
    BigDecimal b1 = new BigDecimal(v1);
    BigDecimal b2 = new BigDecimal(v2);
    return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
    * 取余数 BigDecimal
    *
    * @param v1 被除数
    * @param v2 除数
    * @param scale 小数点后保留几位
    * @return 余数
    */
    public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
    if (scale < 0) {
    throw new IllegalArgumentException(
    "The scale must be a positive integer or zero");
    }
    return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
    * 比较大小
    *
    * @param v1 被比较数
    * @param v2 比较数
    * @return 如果v1 大于v2 则 返回true 否则false
    */
    public static boolean compare(String v1, String v2) {
    BigDecimal b1 = new BigDecimal(v1);
    BigDecimal b2 = new BigDecimal(v2);
    int bj = b1.compareTo(b2);
    boolean res;
    if (bj > 0)
    res = true;
    else
    res = false;
    return res;
    }
    }