java中数字基本运算、金额运算精度问题小结
2016-07-26 18:14
706 查看
一、前言
在我们日常工作中,经常会有涉及到数字的运算,其中金额的运算尤其重要且敏感,因为金额的运算若不注意处理的话,很容易因为精度的丢失,从而导致最终数据的异常,造成严重的系统错误。本文将对java中金额的运算处理进行简单小结。
二、NumberFormat类、DecimalFormat类、BigDecimal类简介
1、NumberFormat类
NumberFormat类是所有数值格式的抽象基类,它继承了Format抽象类。NumberFormat类提供了格式化和分析数值的接口,还提供了一些方法来确定哪些语言环境具有数值格式,以及它们的名称是什么。其常用的方法说明如下:
另外,这几个方法都有相应的通过Locale类指定当前环境的方法。
例1:
输出
注意:格式化后,保留位数小数位后是四舍五入的。
当然,如果说你想对某一个数字精确保留指定位数的话,可以通过相关参数来设置。
例2:
输出
2、DecimalFormat类
DecimalFormat类是NumberFormat的一个具体子类,用于格式化十进制数字,通常用于涉及高精度的运算。DecimalFormat类主要靠 # 和 0 两种占位符号来指定数字长度;0 表示如果位数不足则以 0 填充,# 表示只要有可能就把数字拉上这个位置。
例3:
输出
备注:用#和0的唯一区别是0在数位不足时会自动补足。更多Format模式详情请参考JDK文档。
3、BigDecimal类
float、double类型的主要设计目标是为了科学计算和工程计算,并没有提供完全精确的结果;java.math包下的BigDecimal类则可以满足高精度运算结果,通常用于商业上的精确运算。
BigDecimal类运算结果精确,也可用于大数的运算,但BigDecimal类的运算是通过构造函数创建运算对象,然后对对象进行运算,因此,其不能像 int 、float 、double 、long 等数据类型的数字,直接使用+ 、- 、* 、/ 等算术运算符对其对象进行数学运算 ,而必须调用其相对应的方法进行运算。
其常用构造方法如下:
其中,因为String构造方法的结果是完全可预知的, 所以通常建议优先使用。
其常用运算方法如下:
例4:
输出
三、处理数字精度问题常用方法
通过以上NumberFormat类、DecimalFormat类、BigDecimal类这3个类的简单介绍,我们可以知道,在不用的应用场景下,通过灵活运用这3个类及相关类,我们就可以实现高精度的运算。具体用法可参照以上介绍,下面对数字运算做一些其它方面的补充。
1、在业务系统涉及到金额时,不少人的做法是,金额的单位为元(业务显示一般为元),然后运算、DB存储时均使用double类型、保留2位小数。其实这样很容易造成精度的丢失,更合适的做法是:我们用分来表示金额的单位,在运算的时候直接用int、long数据类型,最后显示的时候再转化成元,这样的话,很大程度上就避免精度丢失的问题了。(金额运算大多数为加、减,乘、除较少)
2、可以合理地运用一些第三方工具包,如:apache.commons.lang3包中的math包,其Fraction 类可用于分数的计算、NumberUtils类可用于数字大小比较、RandomUtils类可用于随机数操作等。
四、总结
1、存储计算金额时,最好直接存整数(表示单位为分、厘、毫),然后直接对整数进行加减运算,最后在最终展示的时候,再换算成所需的单位。
2、需要保证精度的运算最好使用BigDecimal类,因为其精度准确,且与其它基本数据类型装换方便。
3、合理利用一些成熟可靠的第三方工具类,可以给数字相关运算带来很大的便利。
在我们日常工作中,经常会有涉及到数字的运算,其中金额的运算尤其重要且敏感,因为金额的运算若不注意处理的话,很容易因为精度的丢失,从而导致最终数据的异常,造成严重的系统错误。本文将对java中金额的运算处理进行简单小结。
二、NumberFormat类、DecimalFormat类、BigDecimal类简介
1、NumberFormat类
NumberFormat类是所有数值格式的抽象基类,它继承了Format抽象类。NumberFormat类提供了格式化和分析数值的接口,还提供了一些方法来确定哪些语言环境具有数值格式,以及它们的名称是什么。其常用的方法说明如下:
//返回当前缺省语言环境的缺省数值格式 public final static NumberFormat getInstance(); //返回当前缺省语言环境的通用格式 public final static NumberFormat getCurrencyInstance(); //返回当前缺省语言环境的通用数值格式 public final static NumberFormat getNumberInstance(); //返回当前缺省语言环境的百分比格式 public final static NumberFormat getPercentInstance();
另外,这几个方法都有相应的通过Locale类指定当前环境的方法。
例1:
//输出格式化后的数字 public class NumberTest { public static void main(String[] args) { double a = 12345.123456; double b = 0.123456; double c = 12345.67896789; double d = 0.125555; String s1 = NumberFormat.getInstance().format(a); String s2 = NumberFormat.getCurrencyInstance().format(a); String s3 = NumberFormat.getNumberInstance().format(a); String s4 = NumberFormat.getPercentInstance().format(b); String s5 = NumberFormat.getInstance().format(c); String s6 = NumberFormat.getPercentInstance().format(d); System.out.println("s1->" + s1); System.out.println("s2->" + s2); System.out.println("s3->" + s3); System.out.println("s4->" + s4); System.out.println("s5->" + s5); System.out.println("s6->" + s6); } }
输出
s1->12,345.123 s2->¥12,345.12 s3->12,345.123 s4->12% s5->12,345.679 s6->13%
注意:格式化后,保留位数小数位后是四舍五入的。
当然,如果说你想对某一个数字精确保留指定位数的话,可以通过相关参数来设置。
例2:
public class NumberTest { public static void main(String[] args) { double a = 12345.6789; double b = 1.2; NumberFormat format = NumberFormat.getInstance(); //设置数值的整数部分允许的最大位数 format.setMaximumIntegerDigits(3); //设置数值的整数部分允许的最小位数 format.setMinimumIntegerDigits(3); // 设置数值的小数部分允许的最大位数 format.setMaximumFractionDigits(3); //设置数值的小数部分允许的最小位数 format.setMinimumFractionDigits(3); System.out.println("a->" + format.format(a)); System.out.println("b->" + format.format(b)); } }
输出
a->345.679 b->001.200
2、DecimalFormat类
DecimalFormat类是NumberFormat的一个具体子类,用于格式化十进制数字,通常用于涉及高精度的运算。DecimalFormat类主要靠 # 和 0 两种占位符号来指定数字长度;0 表示如果位数不足则以 0 填充,# 表示只要有可能就把数字拉上这个位置。
例3:
public class NumberTest { public static void main(String[] args) { double a = 123.456789; double b = 1.2; double c = 0.12345; long d = 123456789; //最少取2位整数,整数不足部分以0填补 String s1 = new DecimalFormat("00").format(a); //最少取1位整数、取3位小数 String s2 = new DecimalFormat("0.000").format(a); //最少取2位整数、取3位小数,位数不足以0填补 String s3 = new DecimalFormat("00.000").format(b); //取所有整数部分 String s4 = new DecimalFormat("#").format(a); //以百分比方式计数,并最多取两位小数 String s5 = new DecimalFormat("#.##%").format(c); //以百分比方式计数,且整数部分、小数部分都保留2位,位数不足以0填补 String s6 = new DecimalFormat("00.00%").format(b); //"\u2030"表示乘以1000并显示为千分数,要放在最后 String s7 = new DecimalFormat("00.00\u2030").format(c); //显示为科学计数法,并取5位小数 String s21 = new DecimalFormat("#.#####E0").format(d); //显示为2位整数的科学计数法,并取4位小数 String s22 = new DecimalFormat("00.####E0").format(d); //每3位以逗号进行分隔 String s23 = new DecimalFormat(",###").format(d); //每3位以逗号进行分隔,且最少三位 String s24 = new DecimalFormat(",000").format(b); //将格式嵌入文本 String s25 = new DecimalFormat("嵌入的数字是,###这个数").format(d); //用#和0的唯一区别是0在数位不足时会自动补足 String s31 = new DecimalFormat("00.00").format(a); String s32 = new DecimalFormat("##.##").format(a); String s33 = new DecimalFormat("00.00").format(b); String s34 = new DecimalFormat("##.##").format(b); //可以用applyPattern()方法修改Format的模式 DecimalFormat sf = new DecimalFormat("00"); String s41 = sf.format(a); sf.applyPattern("0.000"); String s42 = sf.format(a); System.out.println("s1->" + s1); System.out.println("s2->" + s2); System.out.println("s3->" + s3); System.out.println("s4->" + s4); System.out.println("s5->" + s5); System.out.println("s6->" + s6); System.out.println("s7->" + s7); System.out.println("s21->" + s21); System.out.println("s22->" + s22); System.out.println("s23->" + s23); System.out.println("s24->" + s24); System.out.println("s25->" + s25); System.out.println("s31->" + s31); System.out.println("s32->" + s32); System.out.println("s33->" + s33); System.out.println("s34->" + s34); System.out.println("s41->" + s41); System.out.println("s42->" + s42); } }
输出
s1->123 s2->123.457 s3->01.200 s4->123 s5->12.34% s6->120.00% s7->123.45‰ s21->1.23457E8 s22->12.3457E7 s23->123,456,789 s24->001 s25->嵌入的数字是123,456,789这个数 s31->123.46 s32->123.46 s33->01.20 s34->1.2 s41->123 s42->123.457
备注:用#和0的唯一区别是0在数位不足时会自动补足。更多Format模式详情请参考JDK文档。
3、BigDecimal类
float、double类型的主要设计目标是为了科学计算和工程计算,并没有提供完全精确的结果;java.math包下的BigDecimal类则可以满足高精度运算结果,通常用于商业上的精确运算。
BigDecimal类运算结果精确,也可用于大数的运算,但BigDecimal类的运算是通过构造函数创建运算对象,然后对对象进行运算,因此,其不能像 int 、float 、double 、long 等数据类型的数字,直接使用+ 、- 、* 、/ 等算术运算符对其对象进行数学运算 ,而必须调用其相对应的方法进行运算。
其常用构造方法如下:
序号 | 方法 | 描述 |
---|---|---|
1 | public BigDecimal(double val); | 将double表示形式转换为BigDecimal |
2 | public BigDecimal(int val); | 将int表示形式转换为BigDecimal |
3 | public BigDecimal(long val); | 将long表示形式转换为BigDecimal |
4 | public BigDecimal(String val); | 将String表示形式转换为BigDecimal |
其常用运算方法如下:
序号 | 方法 | 描述 |
---|---|---|
1 | public BigDecimal add(BigDecimal augend); | 加法 |
2 | public BigDecimal subtract(BigDecimal subtrahend); | 减法 |
3 | public BigDecimal multiply(BigDecimal multiplicand); | 乘法 |
4 | public BigDecimal divide(BigDecimal divisor); | 除法; 如果准确的商值没有无穷的十进制扩展,抛ArithmeticException异常 |
5 | public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode); | 除法;设置精确位数、保留位数的策略。RoundingMode为舍入模式,更多舍入模式请查阅API文档 |
6 | public int compareTo(BigDecimal val); | 比较;当此 BigDecimal 在数字上小于、等于或大于 val 时,返回 -1、0 或 1 |
7 | public int scale(); | 返回此 BigDecimal 的标度(即小数点后位数) |
8 | public BigDecimal setScale(int newScale, RoundingMode roundingMode); | 返回 BigDecimal,其标度为指定值,其非标度值通过此 BigDecimal 的非标度值乘以或除以十的适当次幂来确定,以维护其总值 |
public class NumberTest { public static void main(String[] args) { BigDecimal a = new BigDecimal("12.345"); BigDecimal b = new BigDecimal("6.78"); String s1 = a.add(b).toString(); String s2 = a.subtract(b).toString(); String s3 = a.multiply(b).toString(); String s4 = a.divide(b,5,RoundingMode.HALF_UP).toString(); //四舍五入 String s5 = new BigDecimal("10").divide(new BigDecimal("4")).toString(); //商要是有限位数,否侧会抛异常 System.out.println("s1:a+b=" + s1); System.out.println("s2:a-b=" + s2); System.out.println("s3:a*b=" + s3); System.out.println("s4:a/b=" + s4); System.out.println("s5->" + s5); } }
输出
s1:a+b=19.125 s2:a-b=5.565 s3:a*b=83.69910 s4:a/b=1.82080 s5->2.5
三、处理数字精度问题常用方法
通过以上NumberFormat类、DecimalFormat类、BigDecimal类这3个类的简单介绍,我们可以知道,在不用的应用场景下,通过灵活运用这3个类及相关类,我们就可以实现高精度的运算。具体用法可参照以上介绍,下面对数字运算做一些其它方面的补充。
1、在业务系统涉及到金额时,不少人的做法是,金额的单位为元(业务显示一般为元),然后运算、DB存储时均使用double类型、保留2位小数。其实这样很容易造成精度的丢失,更合适的做法是:我们用分来表示金额的单位,在运算的时候直接用int、long数据类型,最后显示的时候再转化成元,这样的话,很大程度上就避免精度丢失的问题了。(金额运算大多数为加、减,乘、除较少)
2、可以合理地运用一些第三方工具包,如:apache.commons.lang3包中的math包,其Fraction 类可用于分数的计算、NumberUtils类可用于数字大小比较、RandomUtils类可用于随机数操作等。
四、总结
1、存储计算金额时,最好直接存整数(表示单位为分、厘、毫),然后直接对整数进行加减运算,最后在最终展示的时候,再换算成所需的单位。
2、需要保证精度的运算最好使用BigDecimal类,因为其精度准确,且与其它基本数据类型装换方便。
3、合理利用一些成熟可靠的第三方工具类,可以给数字相关运算带来很大的便利。
相关文章推荐
- springmvc+mybatis高效接口编程
- MAC系统里JDK版本切换
- springMVC 基础注意
- IO流4(补充
- 160726Java学习
- Struts2之文件上传
- Javaweb学习总结(四)--eclipse中使用github之public key的生成和设置
- java copyOnWrite
- Selenium java mac 自动化测试
- spring aop expression支持多个表达式配置
- java删除文件夹
- Java transient关键字使用小记
- eclipse中本地一个项目引用另一个项目的方法
- 序列化Serializable
- spring整合hibernate扫描多个不同包下的实体类
- 对象序列化
- java过滤敏感词实现字符串替换功能
- Javaweb学习总结(三)--Eclipse中导入github上的项目
- Demo_java_集合Collection
- 【javase复习】## day1 标识符、常量、变量、运算符 ##