简介
说明
本文用实例介绍Java中的BigDecimal。包括:用法、使用BigDecimal的场景(为什么使用BigDecimal),使用BigDecimal应该注意的问题。
使用BigDecimal的场景
典型场景是表示金额。
对于金额而言,BigDecimal是最好的选择。假如在面试中有面试官问到金额用什么类型表示,若回答BigDecimal,此题过关;若回答double、float之类的,结果很可能就是:回去等通知吧😂
在数据库中,对应的类型是decimal,例如:
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; import java.math.RoundingMode; 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, RoundingMode.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; import java.math.RoundingMode; 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, RoundingMode.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)
所以,必须指定保留的位数,以及如何四舍五入。详见下方“四舍五入”
四舍五入(BigDecimal类型)
java.math.RoundingMode有如下模式:
模式 | 说明 | 示例 |
UP | 舍入远离零的舍入模式。 始终对非零舍弃部分前面的数字加 1 | 2.36 -> 2.4 |
DOWN | 接近零的舍入模式。 从不对舍弃部分前面的数字加1,即截短。 | 2.36 -> 2.3 |
CEILING | 接近正无穷大的舍入模式。 如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同; 如果为负,则舍入行为与 ROUND_DOWN 相同。 相当于是 ROUND_UP 和 ROUND_DOWN 的合集 | 2.36 -> 2.4 -2.36 -> -2.3 |
FLOOR | 接近负无穷大的舍入模式。 如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同; 如果为负,则舍入行为与 ROUND_UP 相同。 与 ROUND_CEILING 正好相反 | 2.36 -> 2.3 -2.36 -> -2.4 |
HALF_UP | 四舍五入 | 2.35 -> 2.4 |
HALF_DOWN | 五舍六入。 | 2.35 -> 2.3 |
HALF_EVEN | 如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同(四舍五入); 如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同(五舍六入)。 | 1.15 -> 1.1,1.25 -> 1.2 |
UNNECESSARY | 断言请求的操作具有精确的结果,因此不需要舍入。 如果对获得精确结果的操作指定此舍入模式,则抛出 ArithmeticException。 |
也可以用BigDecimal里的如下模式常量:
- ROUND_UP
- ROUND_DOWN
- ROUND_CEILING
- ROUND_FLOOR
- ROUND_HALF_UP
- ROUND_HALF_DOWN
- ROUND_HALF_EVEN
- ROUND_UNNECESSARY
实例
package com.example.a; import java.math.BigDecimal; import java.math.RoundingMode; public class Demo { public static void main(String[] args) { BigDecimal b = new BigDecimal("2.35"); System.out.println("原始:" + b); System.out.println("UP:" + b.setScale(1, RoundingMode.UP)); System.out.println("DOWN:" + b.setScale(1, RoundingMode.DOWN)); System.out.println("CEILING:" + b.setScale(1, RoundingMode.CEILING)); System.out.println("FLOOR:" + b.setScale(1, RoundingMode.FLOOR)); System.out.println("HALF_UP:" + b.setScale(1, RoundingMode.HALF_UP)); System.out.println("HALF_DOWN:" + b.setScale(1, RoundingMode.HALF_DOWN)); System.out.println("HALF_EVEN:" + b.setScale(1, RoundingMode.HALF_EVEN)); // 下边这个等价于:b.setScale(1); System.out.println("UNNECESSARY:" + b.setScale(1, RoundingMode.UNNECESSARY)); } }
结果
原始:2.35 UP:2.4 DOWN:2.3 CEILING:2.4 FLOOR:2.3 HALF_UP:2.4 HALF_DOWN:2.3 HALF_EVEN:2.4 Exception in thread "main" java.lang.ArithmeticException: Rounding necessary at java.math.BigDecimal.commonNeedIncrement(BigDecimal.java:4179) at java.math.BigDecimal.needIncrement(BigDecimal.java:4235) at java.math.BigDecimal.divideAndRound(BigDecimal.java:4143) at java.math.BigDecimal.setScale(BigDecimal.java:2455) at java.math.BigDecimal.setScale(BigDecimal.java:2389) at com.example.a.Demo.main(Demo.java:18)
比较大小
简介
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”。 |
保留位数(String类型)
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)); System.out.println("#.000 :" + new DecimalFormat("#.000").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 :12.346 000.000 :012.346 ###.### :12.346 #.##% :1234.57% #.##‰ :12345.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.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
请先
!