`
高军威
  • 浏览: 181026 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

BigDecimal商业计算详解

阅读更多
1、浮点数精确计算
   胜利油田三流合一项目中一直存在一个问题,就是每次报表统计的物资金额和实际的金额要差那么几分钱,和实际金额不一致,让客户觉得总是不那么舒服,原因是因为我们使用java的浮点类型double来定义物资金额,并且在报表统计中我们经常要进行一些运算,但Java中浮点数(double、float)的计算是非精确计算,请看下面一个例子:

       System.out.println(0.05 + 0.01);
    System.out.println(1.0 - 0.42);
    System.out.println(4.015 * 100);
    System.out.println(123.3 / 100);
你的期望输出是什么?可实际的输出确实这样的:
       0.060000000000000005
       0.5800000000000001
       401.49999999999994
       1.2329999999999999
   这个问题就非常严重了,如果你有123.3元要购买商品,而计算机却认为你只有123.29999999999999元,钱不够,计算机拒绝交易。

2、四舍五入
    是否可以四舍五入呢?当然可以,习惯上我们本能就会这样考虑,但四舍五入意味着误差,商业运算中可能意味着错误,同时Java中也没有提供保留指定位数的四舍五入方法,只提供了一个Math.round(double d)和Math.round(float f)的方法,分别返回长整型和整型值。round方法不能设置保留几位小数,我们只能象这样(保留两位):
public double round(double value){ 
	return Math.round( value * 100 ) / 100.0; 
}


但非常不幸的是,上面的代码并不能正常工作,给这个方法传入4.015它将返回4.01而不是4.02,如我们在上面看到的

4.015 * 100 = 401.49999999999994

因此如果我们要做到精确的四舍五入,这种方法不能满足我们的要求。

还有一种方式是使用java.text.DecimalFormat,但也存在问题,format采用的舍入模式是ROUND_HALF_DOWN(舍入模式在下面有介绍),比如说4.025保留两位小数会是4.02,因为.025距离” nearest neighbor”(.02和.03)长度是相等,向下舍入就是.02,如果是4.0251那么保留两位小数就是4.03。

System.out.println(new java.text.DecimalFormat("0.00").format(4.025)); 
System.out.println(new java.text.DecimalFormat("0.00").format(4.0251)); 

输出是
4.02
4.03

3、浮点数输出(科学记数法)
Java浮点型数值在大于9999999.0就自动转化为科学记数法来表示,我们看下面的例子:
    System.out.println(999999999.04);
    System.out.println(99999999.04);
    System.out.println(10000000.01);
    System.out.println(9999999.04);
输出的结果如下:

    9.9999999904E8
    9.999999904E7
    1.000000001E7
    9999999.04
   但有时我们可能不需要科学记数法的表示方法,需要转换为字符串,还不能直接用toString()等方法转换,很烦琐。
BigDecimal介绍(详看附件)

8种舍入模式(Rounding mode):

在银行、帐户、计费等商业领域,BigDecimal提供了精确的数值计算。其中8种舍入方式值得掌握。

1、ROUND_UP
舍入远离零的舍入模式。
在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。
注意,此舍入模式始终不会减少计算值的大小。
2、ROUND_DOWN
接近零的舍入模式。
在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1,即截短)。
注意,此舍入模式始终不会增加计算值的大小。
3、ROUND_CEILING
接近正无穷大的舍入模式。
如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;
如果为负,则舍入行为与 ROUND_DOWN 相同。
注意,此舍入模式始终不会减少计算值。
4、ROUND_FLOOR
接近负无穷大的舍入模式。
如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同;
如果为负,则舍入行为与 ROUND_UP 相同。
注意,此舍入模式始终不会增加计算值。
5、ROUND_HALF_UP
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。
如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。
注意,这是我们大多数人在小学时就学过的舍入模式(四舍五入)。
6、ROUND_HALF_DOWN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。
如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同(五舍六入)。
7、ROUND_HALF_EVEN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同;
如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。
注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。
此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况。
如果前一位为奇数,则入位,否则舍去。
以下例子为保留小数点1位,那么这种舍入方式下的结果。
1.15>1.2 1.25>1.2
8、ROUND_UNNECESSARY
断言请求的操作具有精确的结果,因此不需要舍入。
如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。

工具类提供:
这里我们提供了一个工具类,定义浮点数的加、减、乘、除和四舍五入等运算方法。以供参考。

package com.test;

import java.math.BigDecimal;

/**
 * <li>由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 确的浮点数运算,包括加减乘除和四舍五入。</li>
 * <li>推荐使用 String 参数类型,这样计算结果是最精确的</li>
 */
public class Arith {

	// 默认除法运算精度
	private static final int DEF_DIV_SCALE = 10;

	// 这个类不能实例化
	private Arith() {
	}

	/**
	 * 提供精确的加法运算。
	 * @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 double add(String v1, String v2) {
		BigDecimal b1 = new BigDecimal(v1);
		BigDecimal b2 = new BigDecimal(v2);
		return b1.add(b2).doubleValue();
	}

	/**
	 * 提供精确的减法运算。
	 * @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 double sub(String v1, String v2) {
		BigDecimal b1 = new BigDecimal(v1);
		BigDecimal b2 = new BigDecimal(v2);
		return b1.subtract(b2).doubleValue();
	}

	/**
	 * 提供精确的乘法运算。
	 * @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 double mul(String v1, String v2) {
		BigDecimal b1 = new BigDecimal(v1);
		BigDecimal b2 = new BigDecimal(v2);
		return b1.multiply(b2).doubleValue();
	}
	
	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位元,以后的数字四捨五入。
	 * @param v1 被除数
	 * @param v2 除数
	 * @return 两个参数的商
	 */
	public static double div(double v1, double v2) {
		return divide(v1, v2, DEF_DIV_SCALE);
	}
	
	/**
	 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
	 * 定精度,以后的数字四舍五入。舍入模式采用ROUND_HALF_UP
	 * @param v1
	 * @param v2
	 * @param scale 表示需要精确到小数点以后几位
	 * @return double 两个参数的商<br/>
	 */
	public static double divide(double v1,double v2, int scale)
	{
		return divide(v1, v2, scale, BigDecimal.ROUND_HALF_UP);
	}
	
	/**
	 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 
	 * 定精度,以后的数字四舍五入。舍入模式采用用户指定舍入模式
	 * @param v1
	 * @param v2
	 * @param scale 表示需要精确到小数点以后几位
	 * @param round_mode 表示用户指定的舍入模式
	 * @return double 两个参数的商<br/>
	 */
	public static double divide(double v1,double v2,int scale, int round_mode){
		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, round_mode).doubleValue();
	}
	
	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位元,以后的数字四捨五入。
	 * @param v1 被除数
	 * @param v2 除数
	 * @return 两个参数的商
	 */
	public static double div(String v1, String v2) {
		return divide(v1, v2, DEF_DIV_SCALE);
	}
	
	/**
	 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
	 * 定精度,以后的数字四舍五入。舍入模式采用ROUND_HALF_UP
	 * @param v1
	 * @param v2
	 * @param scale 表示需要精确到小数点以后几位
	 * @return double 两个参数的商<br/>
	 */
	public static double divide(String v1,String v2, int scale)
	{
		return divide(v1, v2, scale, BigDecimal.ROUND_HALF_UP);
	}
	
	/**
	 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 
	 * 定精度,以后的数字四舍五入。舍入模式采用用户指定舍入模式
	 * @param v1
	 * @param v2
	 * @param scale 表示需要精确到小数点以后几位
	 * @param round_mode 表示用户指定的舍入模式
	 * @return double 两个参数的商<br/>
	 */
	public static double divide(String v1,String v2,int scale, int round_mode){
		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.divide(b2, scale, round_mode).doubleValue();
	}

	/**
	 * 提供精确的小数位四舍五入处理,舍入模式采用ROUND_HALF_UP
	 * @param v 需要四舍五入的数字
	 * @param scale 小数点后保留几位
	 * @return 四舍五入后的结果
	 */
	public static double round(double v, int scale) {
		return round(v, scale, BigDecimal.ROUND_HALF_UP);
	}
	
	/**
	 * 提供精确的小数位四舍五入处理
	 * @param v 需要四舍五入的数字
	 * @param scale 小数点后保留几位
	 * @param round_mode 指定的舍入模式
	 * @return double 四舍五入后的结果<br/>
	 */
	public static double round(double v, int scale, int round_mode)
	{
		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, round_mode).doubleValue();
	}
	
	/**
	 * 提供精确的小数位四舍五入处理,舍入模式采用ROUND_HALF_UP
	 * @param v 需要四舍五入的数字
	 * @param scale 小数点后保留几位
	 * @return 四舍五入后的结果
	 */
	public static double round(String v, int scale) {
		return round(v, scale, BigDecimal.ROUND_HALF_UP);
	}
	
	/**
	 * 提供精确的小数位四舍五入处理
	 * @param v 需要四舍五入的数字
	 * @param scale 小数点后保留几位
	 * @param round_mode 指定的舍入模式
	 * @return double 四舍五入后的结果<br/>
	 */
	public static double round(String v, int scale, int round_mode)
	{
		if(scale<0)
		{
			throw new IllegalArgumentException("The scale must be a positive integer or zero");
		}
		BigDecimal b = new BigDecimal(v);
		return b.setScale(scale, round_mode).doubleValue();
	}
}
分享到:
评论

相关推荐

    BigDecimal的计算

    ### BigDecimal的计算 #### 概述 在Java中,`BigDecimal` 类提供了对超过16位有效数字的十进制数进行精确算术运算的能力。它适用于金融计算、科学计算等场景,在这些场景中,使用浮点数(如 `float` 和 `double`)...

    MyEditTextApplication输入框BigDecimal计算价格

    本项目“MyEditTextApplication”专注于使用BigDecimal进行输入框内的价格计算,确保计算结果的高度精确,尤其是在处理小数点后的多位数字时。 BigDecimal是Java提供的一个大数类,它可以用来进行精确的浮点数运算...

    JS的高精度计算(BigDecimal)

    你是否因为JS端的小数计算的精度不足而发愁过呢 其实有个不错的资源可以利用。 引用附件的JS,就可以轻松搞定JS的计算问题了。 使用例 &lt;!-- function //+ alert(new BigDecimal("10").add(new BigDecimal("3E+...

    BigDecimal加减乘除计算

    总之,BigDecimal 是 Java 中用于高精度计算的关键工具,通过其提供的方法和舍入模式,可以确保在财务等对精度有严格要求的场景下进行准确的计算。在实际编程中,应优先使用字符串构造 BigDecimal 对象,并合理设置...

    运用BigDecimal精确计算

    在处理货币计算、金融数据或其他需要精确数值计算的场景中,`BigDecimal`是不可或缺的工具。 1. 创建BigDecimal对象: 创建`BigDecimal`对象通常有两种方式,一是通过`new BigDecimal(String)`构造器,二是通过`...

    Java Bigdecimal使用原理详解

    Java Bigdecimal使用原理详解 Java Bigdecimal是Java语言中用于精确计算的类,它可以完善float和double类无法进行精确计算的缺憾。BigDecimal类位于java.math类包下,提供了多种构造函数和方法来实现精确计算。 ...

    BigDecimal计算

    在Java编程语言中,`BigDecimal`类是用于表示和操作大数(通常涉及财务或精确数学计算)的重要工具。由于浮点数(如`float`和`double`)在计算机中是以近似值存储的,这可能导致计算结果的不精确性,尤其是在进行...

    Java中BigDecimal的基本运算(详解)

    Java中BigDecimal的基本运算详解 Java中的BigDecimal是一种高精度的数据类型,它可以用来表示非常大的整数和小数,提供了丰富的数学运算功能。下面我们将对Java中BigDecimal的基本运算进行详细的介绍。 构造方法 ...

    Java BigDecimal类用法详解

    Java中的`BigDecimal`类是用来表示任意精度的十进制数,尤其适合于需要精确计算的商业和财务场景。它的核心概念包括非标度值(unscaled value)和标度(scale),非标度值是一个任意精度的整数,标度则是小数点后的...

    BigDecimal 加减乘除运算

    float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的...

    Java BigDecimal详解_动力节点Java学院整理

    借用《Effactive Java》这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,...但是,商业计算往往要求结果精确,例如银行存款数额,这时候BigDecimal就派上大用场啦。

    javascript版BigDecimal类库

    总的来说,JavaScript版`BigDecimal`类库为开发者提供了一种解决JavaScript精度问题的工具,使得在进行金融计算、科学计算或其他需要高精度计算的场合下,能够得到准确的结果。通过学习和应用这个库,开发者可以更好...

    bigdecimal

    ### Java中的BigDecimal类详解 在Java编程语言中,当我们处理涉及货币、财务或者任何需要高精度计算的场景时,`BigDecimal` 类是非常重要的工具之一。本文将深入探讨 `BigDecimal` 类的基本概念、特点以及如何使用...

    BigDecimal开n次方根

    复杂的BigDecimal计算,需要开方的式子,可输入结果精确位数

    BigDecimal详解、代码示例和经常遇到的坑

    ### BigDecimal详解、代码示例和经常遇到的坑 #### 一、BigDecimal概述 `BigDecimal`是Java编程语言中`java.math`包内提供的一种高级数据类型,主要用于处理高精度的十进制数值。与基本数据类型`float`或`double`...

    BigDecimal类

    BigDecimal 类详解 BigDecimal 类是 Java 中的一种数值类型,主要用于处理超过 16 位有效数字的数值运算。该类提供了多种构造器和方法,用于创建和操作 BigDecimal 对象。 构造器 BigDecimal 类提供了四种构造器...

    BigDecimal.js.zip

    在实际应用中,BigDecimal.js可以用于电商系统的订单总价计算、金融应用的汇率转换、科学计算等场景,确保计算结果的精确性。 总之,BigDecimal.js是一个强大的JavaScript库,专门用于处理大数的高精度计算,通过...

    BigDecimal工具类.docx

    BigDecimal工具类提供了多种精确的数学运算方法,满足了我们在实际开发中对高精确计算的需求。同时,该工具类还提供了很多实用的方法,使得我们可以更方便地进行数学运算。 知识点: 1. BigDecimal工具类是Java中...

    Java中BigDecimal类的使用详解

    Java中BigDecimal类的使用详解 Java中BigDecimal类是Java.math包中提供的一个API类,用于对超过16位有效位的数进行精确的运算。由于浮点数的精度问题,Java中浮点数的计算会失去一定的精确度。因此,使用BigDecimal...

    BigDecimal使用

    BigDecimal 是 Java 中一个用于处理金融和商业应用的类,其主要功能是提供高精度的数字计算。下面是关于 BigDecimal 的使用方法和注意事项: 1. 导包:在使用 BigDecimal 之前,需要导入 java.math.BigDecimal 包。...

Global site tag (gtag.js) - Google Analytics