简介
说明
本文用实例介绍Java中的BigDecimal。包括:用法、使用BigDecimal的场景(为什么使用BigDecimal),使用BigDecimal应该注意的问题。
使用BigDecimal的场景
典型场景是表示金额。
对于金额而言,BigDecimal是最好的选择。假如在面试中有面试官问到金额用什么类型表示,若回答BigDecimal,此题过关;若回答double、float之类的,结果很可能就是:回去等通知吧????
在数据库中,对应的类型是bigdecimal,例如:
BigDecimal与double
简介
特点 | BigDecimal | double |
精准度 | 绝对精准 | 小数部分极大可能损失精度 |
数字长度 | 可以处理大于16位长度的数 | 最长只能处理16为长度的数 |
Java中提供了java.math.BigInteger 类和 java.math.BigDecimal 类,用于处理超过16位有效位的数字。双精度浮点型变量double可以处理16位有效数。
一般情况下,对于那些不需要准确计算精度的数字,可直接用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以如果需要精确计算的结果,必须使用BigDecimal类。
实例1:加法
package com.example.a;
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
double d1 = 0.3;
double d2 = 0.2;
System.out.println("double:\t\t 0.3 - 0.2 = " + (d1 - d2));
float f1 = 0.3f;
float f2 = 0.2f;
System.out.println("float:\t\t 0.3 - 0.2 = " + (f1 - f2));
BigDecimal bd1 = new BigDecimal("0.3");
BigDecimal bd2 = new BigDecimal("0.2");
System.out.println("BigDecimal:\t 0.3 - 0.2 = " + (bd1.subtract(bd2)));
}
}
执行结果
double: 0.3 - 0.2 = 0.09999999999999998
float: 0.3 - 0.2 = 0.10000001
BigDecimal: 0.3 - 0.2 = 0.1
可见:只有BigDecimal保持了精度。
实例2:除法
package com.example.a;
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
double d1 = 10;
double d2 = 3;
System.out.println("double:\t\t 10 / 3 = " + (d1 / d2));
float f1 = 10f;
float f2 = 3f;
System.out.println("float:\t\t 10 / 3 = " + (f1 / f2));
BigDecimal bd1 = new BigDecimal("10");
BigDecimal bd2 = new BigDecimal("3");
System.out.println("BigDecimal:\t 10 / 3 = " + (bd1.divide(bd2, 3, BigDecimal.ROUND_HALF_UP)));
}
}
结果
double: 10 / 3 = 3.3333333333333335
float: 10 / 3 = 3.3333333
BigDecimal: 10 / 3 = 3.333
bd1.divide(bd2, 3, BigDecimal.ROUND_HALF_UP))
结果
double: 10 / 3 = 3.3333333333333335
float: 10 / 3 = 3.3333333
BigDecimal: 10 / 3 = 3.333
构造方法
本处重点介绍带小数的构造方法。其他通过int、long等进行构造的方法不赘述了。
BigDecimal BigDecimal(String s); //推荐使用
static BigDecimal valueOf(double d); //推荐使用
BigDecimal BigDecimal(double d); //不推荐使用,精度不能保证
实例
package com.example.a;
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
BigDecimal bigDecimal1 = new BigDecimal("1.11");
BigDecimal bigDecimal2 = BigDecimal.valueOf(1.11);
BigDecimal bigDecimal3 = new BigDecimal(1.11);
System.out.println(bigDecimal1);
System.out.println(bigDecimal2);
System.out.println(bigDecimal3);
}
}
结果:
1.11
1.11
1.1100000000000000976996261670137755572795867919921875
加减乘除
方法 | 描述 |
add(BigDecimal) | BigDecimal对象中的值相加,然后返回这个对象 |
subtract(BigDecimal) | BigDecimal对象中的值相减,然后返回这个对象 |
multiply(BigDecimal) | BigDecimal对象中的值相乘,然后返回这个对象 |
divide(BigDecimal) | BigDecimal对象中的值相除,然后返回这个对象 |
实例
package com.example.a;
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("2.34");
BigDecimal b2 = new BigDecimal("1.11");
System.out.println(b1.add(b2));
System.out.println(b1.subtract(b2));
System.out.println(b1.multiply(b2));
System.out.println(b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP));
}
}
结果
3.45
1.23
2.5974
2.11
注意
对于BigDecimal来说,如果结果除不尽,且没有设置进位的状态值,那就会抛出异常。
比如:
package com.example.a;
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("10");
BigDecimal b2 = new BigDecimal("3");
System.out.println(b1.divide(b2));
}
}
执行结果
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 com.example.a.Demo.main(Demo.java:10)
所以,必须指定保留的位数,以及如何四舍五入。详见下方“四舍五入”
四舍五入
模式 | 说明 | 示例 |
ROUND_UP | 舍入远离零的舍入模式。 始终对非零舍弃部分前面的数字加 1 | 2.36 -> 2.4 |
ROUND_DOWN | 接近零的舍入模式。 从不对舍弃部分前面的数字加1,即截短。 | 2.36 -> 2.3 |
ROUND_CEILING | 接近正无穷大的舍入模式。 如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同; 如果为负,则舍入行为与 ROUND_DOWN 相同。 相当于是 ROUND_UP 和 ROUND_DOWN 的合集 | 2.36 -> 2.4 -2.36 -> -2.3 |
ROUND_FLOOR | 接近负无穷大的舍入模式。 如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同; 如果为负,则舍入行为与 ROUND_UP 相同。 与 ROUND_CEILING 正好相反 | 2.36 -> 2.3 -2.36 -> -2.4 |
ROUND_HALF_UP | 四舍五入 | 2.35 -> 2.4 |
ROUND_HALF_DOWN | 五舍六入。 | 2.35 -> 2.3 |
ROUND_HALF_EVEN | 如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同(四舍五入); 如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同(五舍六入)。 | 1.15 -> 1.1,1.25 -> 1.2 |
ROUND_UNNECESSARY | 断言请求的操作具有精确的结果,因此不需要舍入。 如果对获得精确结果的操作指定此舍入模式,则抛出 ArithmeticException。 |
实例
package com.example.a;
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
BigDecimal b = new BigDecimal("2.35");
// setScale(1) 表示保留一位小数
System.out.println("ROUND_UP,结果:" + b.setScale(1, BigDecimal.ROUND_UP));
System.out.println("ROUND_DOWN,结果:" + b.setScale(1, BigDecimal.ROUND_DOWN));
System.out.println("ROUND_CEILING,结果:" + b.setScale(1, BigDecimal.ROUND_CEILING));
System.out.println("ROUND_FLOOR,结果:" + b.setScale(1, BigDecimal.ROUND_FLOOR));
System.out.println("ROUND_HALF_UP,结果:" + b.setScale(1, BigDecimal.ROUND_HALF_UP));
System.out.println("ROUND_HALF_DOWN,结果:" + b.setScale(1, BigDecimal.ROUND_HALF_DOWN));
System.out.println("ROUND_HALF_EVEN,结果:" + b.setScale(1, BigDecimal.ROUND_HALF_EVEN));
System.out.println("ROUND_UNNECESSARY,结果:" + b.setScale(1, BigDecimal.ROUND_UNNECESSARY));
}
}
结果
ROUND_UP,结果:2.4
ROUND_DOWN,结果:2.3
ROUND_CEILING,结果:2.4
ROUND_FLOOR,结果:2.3
ROUND_HALF_UP,结果:2.4
ROUND_HALF_DOWN,结果:2.3
ROUND_HALF_EVEN,结果:2.4
Exception in thread "main" java.lang.ArithmeticException: Rounding necessary
at java.math.BigDecimal.commonNeedIncrement(BigDecimal.java:4151)
at java.math.BigDecimal.needIncrement(BigDecimal.java:4207)
at java.math.BigDecimal.divideAndRound(BigDecimal.java:4115)
at java.math.BigDecimal.setScale(BigDecimal.java:2455)
at com.example.a.Demo.main(Demo.java:16)
比较大小
简介
a.compareTo(b)
结果:a > b 返回 1;a = b 返回 0;a < b 返回 -1
实例
package com.example.a;
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("2.34");
BigDecimal b2 = new BigDecimal("1.11");
System.out.println(b1.compareTo(b2));
}
}
结果
1
格式化(保留位数等)
DecimalFormat 是 NumberFormat 的一个具体子类,用于格式化十进制数字。
符号 | 位置 | 说明 |
0 | 数字 | 阿拉伯数字,如果不存在则显示0 |
# | 数字 | 阿拉伯数字,如果不存在不显示0 |
. | 数字 | 小数分隔符或货币小数分隔符 |
, | 数字 | 分组分隔符 |
E | 数字 | 分隔科学计数法中的尾数和指数。在前缀或后缀中无需加引号。 |
- | 数字 | 负号 |
; | 子模式边界 | 分隔正数和负数子模式 |
% | 前缀或后缀 | 乘以 100 并显示为百分数 |
\u2030 | 前缀或后缀 | 乘以 1000 并显示为千分数 |
¤(\u00A4) | 前缀或后缀 | 货币记号,由货币符号替换。 如果两个同时出现,则用国际货币符号替换。如果出现在某个模式中,则使用货币小数分隔符,而不使用小数分隔符。 |
' | 前缀或后缀 | 用于在前缀或或后缀中为特殊字符加引号,例如 "'#'#"将 123 格式化为 "#123"。要创建单引号本身,请连续使用两个单引号:"# o''clock"。 |
数字保留位数(0和#)
package com.example.a;
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class Demo {
public static void main(String[] args) {
String stringNum = "12.345678";
BigDecimal num = new BigDecimal(stringNum);
//取所有整数部分
System.out.println("0 :" + new DecimalFormat("0").format(num));
System.out.println("# :" + new DecimalFormat("#").format(num));
//取一位整数和三位小数
System.out.println("0.000 :" + new DecimalFormat("0.000").format(num));
System.out.println("#.### :" + new DecimalFormat("#.###").format(num));
//取3位整数和三位小数,整数不足部分以0填补。
System.out.println("000.000 :" + new DecimalFormat("000.000").format(num));
//取3位整数和三位小数,整数不足不填补。
System.out.println("###.### :" + new DecimalFormat("###.###").format(num));
//以百分比方式计数,并取两位小数
System.out.println("#.##% :" + new DecimalFormat("#.##%").format(num));
//以千分比方式计数,并取两位小数
System.out.println("#.##\u2030 :" + new DecimalFormat("#.##\u2030").format(num));
}
}
结果
0 :12
# :12
0.000 :12.346
#.### :12.346
000.000 :012.346
###.### :12.346
#.##% :1234.57%
#.##‰ :12345.68‰
科学计数法E
package com.example.a;
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class Demo {
public static void main(String[] args) {
String stringNum = "1234.567";
BigDecimal num = new BigDecimal(stringNum);
System.out.println(new DecimalFormat("0E0").format(num));
System.out.println(new DecimalFormat("0E00").format(num));
System.out.println(new DecimalFormat("#E0").format(num));
System.out.println(new DecimalFormat("##E0").format(num));
System.out.println(new DecimalFormat("###E0").format(num));
System.out.println(new DecimalFormat("####E0").format(num));
System.out.println(new DecimalFormat("#####E0").format(num));
System.out.println(new DecimalFormat("######E0").format(num));
System.out.println(new DecimalFormat("#######E0").format(num));
System.out.println(new DecimalFormat("########E0").format(num));
}
}
执行结果
1E3
1E03
.1E4
12E2
1.23E3
1235E0
1234.6E0
1234.57E0
1234.567E0
1234.567E0
分隔符和减号
分隔符
package com.example.a;
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class Demo {
public static void main(String[] args) {
String stringNum = "1234567.89";
BigDecimal num = new BigDecimal(stringNum);
System.out.println(new DecimalFormat(",###").format(num));
System.out.println(new DecimalFormat(",##").format(num));
}
}
结果
1,234,568
1,23,45,68
减号(负数)
-
表示输出为负数, 要放在最前面
package com.example.a;
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class Demo {
public static void main(String[] args) {
String stringNum = "1234.56789";
BigDecimal num = new BigDecimal(stringNum);
System.out.println(new DecimalFormat("-#.#").format(num));
}
}
结果
-1234.56789
货币
package com.example.a;
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class Demo {
public static void main(String[] args) {
String stringNum = "1234.56789";
BigDecimal num = new BigDecimal(stringNum);
System.out.println(new DecimalFormat(",000.00¤").format(num));
System.out.println(new DecimalFormat(",000.¤00").format(num));
System.out.println(new DecimalFormat("¤,000.00").format(num));
System.out.println(new DecimalFormat(",00¤0.¤00").format(num));
System.out.println(new DecimalFormat("¤,000.¤00").format(num));
System.out.println(new DecimalFormat(",000.00¤¤").format(num));
}
}
结果
1,234.57¥
1,234.57¥
¥1,234.57
1,234.57¥¥
¥1,234.57¥
1,234.57CNY
引号
' 用于引用特殊的字符,作为前缀或后缀。
package com.example.a;
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class Demo {
public static void main(String[] args) {
String stringNum = "1234.56789";
BigDecimal num = new BigDecimal(stringNum);
//使用#本身作为前缀或后缀
System.out.println(new DecimalFormat("'#'0.00").format(num));//#4.57
//使用'本身作为前缀或后缀
System.out.println(new DecimalFormat("''0.00").format(num));//'4.57
}
}
结果
#1234.57
'1234.57
其他网址
BigDecimal 使用总结--
BigDecimal使用(整理) - 简书