说在前面的话
        先祝各位看官在虎年里虎虎生威,财源广进,万事如意!
        Java 语言支持两种基本的浮点类型: float 和 double ,以及与它们对应的包装类 Float 和 Double 。它们都依据 IEEE 754 标准,该标准为 32 位浮点和 64 位双精度浮点二进制小数定义了二进制标准。 
        但是在一些项目中,一些非整数值(如几元和几分这样的小数)需要很精确。所以,不要用浮点数表示精确值。浮点数不是精确值,所以使用它们会导致舍入误差。因此,使用浮点数来试图表示象货币量这样的精确数量不是一个好的想法。使用浮点数来进行元和分计算会得到灾难性的后果。浮点数最好用来表示象测量值这类数值,这类值从一开始就不怎么精确。
        所以一般对double类型进行运算时,做好对结果进行处理,然后拿这个值去做其他事情。 下面我们就用代码来说明一下如何对浮点数进行精度计算,以double为例。
详细代码(注释很详细 不做解释了)  可点击这里下载代码

 /**//*
/**//*
 * Copyright reserved 2010 by AllensLab
 * Copyright reserved 2010 by AllensLab
 * @project AllensLab
 * @project AllensLab
 * @date Feb 22, 2010
 * @date Feb 22, 2010
 */
 */
 package cn.allen.tools;
package cn.allen.tools;

 import java.math.BigDecimal;
import java.math.BigDecimal;


 /** *//**
/** *//**
 * RoundTool
 * RoundTool
 * @author allen
 * @author allen
 * @time 10:05:40 AM Feb 22, 2010
 * @time 10:05:40 AM Feb 22, 2010
 */
 */

 public class RoundTools
public class RoundTools  {
{

 /** *//**
    /** *//**  
 * 对double数据进行取精度.
     * 对double数据进行取精度.  
 * <p>
     * <p>  
 * For example: <br>
     * For example: <br>  
 * double value = 100.345678; <br>
     * double value = 100.345678; <br>  
 * double ret = round(value,4,BigDecimal.ROUND_HALF_UP); <br>
     * double ret = round(value,4,BigDecimal.ROUND_HALF_UP); <br>  
 * ret为100.3457 <br>
     * ret为100.3457 <br>  
 *
     *   
 * @param value
     * @param value  
 *            double数据.
     *            double数据.  
 * @param scale
     * @param scale  
 *            精度位数(保留的小数位数).
     *            精度位数(保留的小数位数).  
 * @param roundingMode
     * @param roundingMode  
 *            精度取值方式.
     *            精度取值方式.  
 * @return 精度计算后的数据.
     * @return 精度计算后的数据.  
 */
     */

 public static double round(double value, int scale, int roundingMode)
    public static double round(double value, int scale, int roundingMode)  {
{
 BigDecimal bd = new BigDecimal(value);
        BigDecimal bd = new BigDecimal(value);
 bd = bd.setScale(scale, roundingMode);
        bd = bd.setScale(scale, roundingMode);
 double d = bd.doubleValue();
        double d = bd.doubleValue();
 bd = null;
        bd = null;
 return d;
        return d;
 }
    }
 
    

 public static BigDecimal roundAgain(double value, int scale, int roundingMode)
    public static BigDecimal roundAgain(double value, int scale, int roundingMode)  {
{
 BigDecimal bd = new BigDecimal(value);
        BigDecimal bd = new BigDecimal(value);
 bd = bd.setScale(scale, roundingMode);
        bd = bd.setScale(scale, roundingMode);
 return bd;
        return bd;
 }
    }
 
    

 /**//* 用于加、减、乘和除的方法给 BigDecimal 值提供了算术运算。由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的 BigDecimal 对象。
    /**//* 用于加、减、乘和除的方法给 BigDecimal 值提供了算术运算。由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的 BigDecimal 对象。
 * 因为创建对象的开销, BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。 */
     * 因为创建对象的开销, BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。 */
 
    

 /** *//**
    /** *//**
 * Addition
     * Addition
 * @param number1
     * @param number1
 * @param number2
     * @param number2
 * @return
     * @return
 * @author allen
     * @author allen
 * @date 10:42:47 AM Feb 22, 2010
     * @date 10:42:47 AM Feb 22, 2010
 */
     */

 public static double add(double number1, double number2, int newScale, int roundingMode)
    public static double add(double number1, double number2, int newScale, int roundingMode)  {
{
 return BigDecimal.valueOf(number1).add(BigDecimal.valueOf(number2)).setScale(newScale, roundingMode).doubleValue();
        return BigDecimal.valueOf(number1).add(BigDecimal.valueOf(number2)).setScale(newScale, roundingMode).doubleValue();
 }
    }
 
    

 public static double add(int newScale, int roundingMode, double number1, double
    public static double add(int newScale, int roundingMode, double number1, double numbers)
 numbers)  {
{
 BigDecimal bd = new BigDecimal(number1);
        BigDecimal bd = new BigDecimal(number1);

 for (double number : numbers)
        for (double number : numbers)  {
{
 bd = bd.add(BigDecimal.valueOf(number).setScale(newScale, roundingMode));
            bd = bd.add(BigDecimal.valueOf(number).setScale(newScale, roundingMode));
 }
        }
 return bd.doubleValue();
        return bd.doubleValue();
 }
    }
 
    

 /** *//**
    /** *//**
 * Subtraction
     * Subtraction
 * @param number1
     * @param number1
 * @param number2
     * @param number2
 * @return
     * @return
 * @author allen
     * @author allen
 * @date 10:45:36 AM Feb 22, 2010
     * @date 10:45:36 AM Feb 22, 2010
 */
     */

 public static double subtract(double number1, double number2, int newScale, int roundingMode)
    public static double subtract(double number1, double number2, int newScale, int roundingMode)  {
{
 return BigDecimal.valueOf(number1).subtract(BigDecimal.valueOf(number2)).setScale(newScale, roundingMode).doubleValue();
        return BigDecimal.valueOf(number1).subtract(BigDecimal.valueOf(number2)).setScale(newScale, roundingMode).doubleValue();
 }
    }
 
    

 /** *//**
    /** *//**
 * Multiplication
     * Multiplication 
 * @param number1
     * @param number1
 * @param number2
     * @param number2
 * @return
     * @return
 * @author allen
     * @author allen
 * @date 10:46:23 AM Feb 22, 2010
     * @date 10:46:23 AM Feb 22, 2010
 */
     */

 public static double multiply(double number1, double number2, int newScale, int roundingMode)
    public static double multiply(double number1, double number2, int newScale, int roundingMode)  {
{
 return BigDecimal.valueOf(number1).multiply(BigDecimal.valueOf(number2)).setScale(newScale, roundingMode).doubleValue();
        return BigDecimal.valueOf(number1).multiply(BigDecimal.valueOf(number2)).setScale(newScale, roundingMode).doubleValue();
 
        
 }
    }
 
    

 /** *//**
    /** *//**
 * Division
     * Division
 * 尽量采用财务常用的四舍六入五取偶 即ROUND_HALF_EVEN
     * 尽量采用财务常用的四舍六入五取偶 即ROUND_HALF_EVEN
 * @param number1
     * @param number1
 * @param number2
     * @param number2
 * @return
     * @return
 * @author allen
     * @author allen
 * @date 10:47:12 AM Feb 22, 2010
     * @date 10:47:12 AM Feb 22, 2010
 */
     */

 public static double divide(double number1, double number2, int scale, int roundingMode)
    public static double divide(double number1, double number2, int scale, int roundingMode)  {
{
 return BigDecimal.valueOf(number1).divide(BigDecimal.valueOf(number2), scale, roundingMode).doubleValue();
        return BigDecimal.valueOf(number1).divide(BigDecimal.valueOf(number2), scale, roundingMode).doubleValue();
 }
    }
 
    

 /** *//**
    /** *//**  
 * 测试用的main方法.
     * 测试用的main方法.  
 *
     *   
 * @param args
     * @param args  
 *            运行参数.
     *            运行参数.  
 */
     */

 public static void main(String[] args)
    public static void main(String[] args)  {
{
 //下面都以保留2位小数为例
        //下面都以保留2位小数为例   
 System.out.println(add(12.341, 12.3449, 2, BigDecimal.ROUND_HALF_EVEN));
        System.out.println(add(12.341, 12.3449, 2, BigDecimal.ROUND_HALF_EVEN));
 System.out.println(add(2, BigDecimal.ROUND_HALF_UP, 12.346, 12.3449, 12.3401, 12.345));
        System.out.println(add(2, BigDecimal.ROUND_HALF_UP, 12.346, 12.3449, 12.3401, 12.345));
 System.out.println(subtract(12.3449, 12.341, 2, BigDecimal.ROUND_HALF_EVEN));
        System.out.println(subtract(12.3449, 12.341, 2, BigDecimal.ROUND_HALF_EVEN));
 System.out.println(multiply(12.3449, 0.01, 2, BigDecimal.ROUND_HALF_UP));
        System.out.println(multiply(12.3449, 0.01, 2, BigDecimal.ROUND_HALF_UP));
 System.out.println(divide(11.341, 12.346, 2, BigDecimal.ROUND_HALF_EVEN));
        System.out.println(divide(11.341, 12.346, 2, BigDecimal.ROUND_HALF_EVEN));
 //ROUND_UP
        //ROUND_UP   
 //只要第2位后面存在大于0的小数,则第2位就+1
        //只要第2位后面存在大于0的小数,则第2位就+1
 System.out.println("-- ROUND_UP -- 只要第2位后面存在大于0的小数,则第2位就+1 --");
        System.out.println("-- ROUND_UP -- 只要第2位后面存在大于0的小数,则第2位就+1 --");
 System.out.println(round(12.3401, 2, BigDecimal.ROUND_UP));//12.35
        System.out.println(round(12.3401, 2, BigDecimal.ROUND_UP));//12.35   
 System.out.println(round(-12.3401, 2, BigDecimal.ROUND_UP));//-12.35
        System.out.println(round(-12.3401, 2, BigDecimal.ROUND_UP));//-12.35   
 //ROUND_DOWN
        //ROUND_DOWN   
 //与ROUND_UP相反
        //与ROUND_UP相反   
 //直接舍弃第2位后面的所有小数
        //直接舍弃第2位后面的所有小数   
 System.out.println("-- ROUND_DOWN -- 直接舍弃第2位后面的所有小数 --");
        System.out.println("-- ROUND_DOWN -- 直接舍弃第2位后面的所有小数 --");
 System.out.println(round(12.349, 2, BigDecimal.ROUND_DOWN));//12.34
        System.out.println(round(12.349, 2, BigDecimal.ROUND_DOWN));//12.34   
 System.out.println(round(-12.349, 2, BigDecimal.ROUND_DOWN));//-12.34
        System.out.println(round(-12.349, 2, BigDecimal.ROUND_DOWN));//-12.34   
 //ROUND_CEILING
        //ROUND_CEILING   
 //如果数字>0 则和ROUND_UP作用一样
        //如果数字>0 则和ROUND_UP作用一样   
 //如果数字<0 则和ROUND_DOWN作用一样
        //如果数字<0 则和ROUND_DOWN作用一样   
 System.out.println("-- OUND_CEILING -- 如果数字>0 则和ROUND_UP作用一样 如果数字<0 则和ROUND_DOWN作用一样 --");
        System.out.println("-- OUND_CEILING -- 如果数字>0 则和ROUND_UP作用一样 如果数字<0 则和ROUND_DOWN作用一样 --");
 System.out.println(round(12.3401, 2, BigDecimal.ROUND_CEILING));//12.35
        System.out.println(round(12.3401, 2, BigDecimal.ROUND_CEILING));//12.35   
 System.out.println(round(-12.349, 2, BigDecimal.ROUND_CEILING));//-12.34
        System.out.println(round(-12.349, 2, BigDecimal.ROUND_CEILING));//-12.34   
 //ROUND_FLOOR
        //ROUND_FLOOR   
 //如果数字>0 则和ROUND_DOWN作用一样
        //如果数字>0 则和ROUND_DOWN作用一样   
 //如果数字<0 则和ROUND_UP作用一样
        //如果数字<0 则和ROUND_UP作用一样   
 System.out.println("-- ROUND_FLOOR -- 如果数字>0 则和ROUND_DOWN作用一样 如果数字<0 则和ROUND_UP作用一样 --");
        System.out.println("-- ROUND_FLOOR -- 如果数字>0 则和ROUND_DOWN作用一样 如果数字<0 则和ROUND_UP作用一样 --");
 System.out.println(round(12.349, 2, BigDecimal.ROUND_FLOOR));//12.34
        System.out.println(round(12.349, 2, BigDecimal.ROUND_FLOOR));//12.34   
 System.out.println(round(-12.3401, 2, BigDecimal.ROUND_FLOOR));//-12.35
        System.out.println(round(-12.3401, 2, BigDecimal.ROUND_FLOOR));//-12.35   
 //ROUND_HALF_UP [这种方法最常用]
        //ROUND_HALF_UP [这种方法最常用]   
 //如果第3位数字>=5,则第2位数字+1
        //如果第3位数字>=5,则第2位数字+1   
 //备注:只看第3位数字的值,不会考虑第3位之后的小数的
        //备注:只看第3位数字的值,不会考虑第3位之后的小数的   
 System.out.println("-- ROUND_HALF_UP -- 如果第3位数字>=5,则第2位数字+1 --");
        System.out.println("-- ROUND_HALF_UP -- 如果第3位数字>=5,则第2位数字+1 --");
 System.out.println(round(12.345, 2, BigDecimal.ROUND_HALF_UP));//12.35
        System.out.println(round(12.345, 2, BigDecimal.ROUND_HALF_UP));//12.35   
 System.out.println(round(12.3449, 2, BigDecimal.ROUND_HALF_UP));//12.34
        System.out.println(round(12.3449, 2, BigDecimal.ROUND_HALF_UP));//12.34   
 System.out.println(round(-12.345, 2, BigDecimal.ROUND_HALF_UP));//-12.35
        System.out.println(round(-12.345, 2, BigDecimal.ROUND_HALF_UP));//-12.35   
 System.out.println(round(-12.3449, 2, BigDecimal.ROUND_HALF_UP));//-12.34
        System.out.println(round(-12.3449, 2, BigDecimal.ROUND_HALF_UP));//-12.34   
 //ROUND_HALF_DOWN
        //ROUND_HALF_DOWN   
 //如果第3位数字>=5,则做ROUND_UP
        //如果第3位数字>=5,则做ROUND_UP   
 //如果第3位数字<5,则做ROUND_DOWN
        //如果第3位数字<5,则做ROUND_DOWN   
 System.out.println("-- ROUND_HALF_DOWN -- 如果第3位数字>=5,则做ROUND_UP,如果第3位数字<5,则做ROUND_DOWN --");
        System.out.println("-- ROUND_HALF_DOWN -- 如果第3位数字>=5,则做ROUND_UP,如果第3位数字<5,则做ROUND_DOWN --");
 System.out.println(round(12.345, 2, BigDecimal.ROUND_HALF_DOWN));//12.35
        System.out.println(round(12.345, 2, BigDecimal.ROUND_HALF_DOWN));//12.35   
 System.out.println(round(12.3449, 2, BigDecimal.ROUND_HALF_DOWN));//12.34
        System.out.println(round(12.3449, 2, BigDecimal.ROUND_HALF_DOWN));//12.34   
 System.out.println(round(-12.345, 2, BigDecimal.ROUND_HALF_DOWN));//-12.35
        System.out.println(round(-12.345, 2, BigDecimal.ROUND_HALF_DOWN));//-12.35   
 System.out.println(round(-12.3449, 2, BigDecimal.ROUND_HALF_DOWN));//-12.34
        System.out.println(round(-12.3449, 2, BigDecimal.ROUND_HALF_DOWN));//-12.34   
 //ROUND_HALF_EVEN
        //ROUND_HALF_EVEN   
 //如果第3位是偶数,则做ROUND_HALF_DOWN
        //如果第3位是偶数,则做ROUND_HALF_DOWN   
 //如果第3位是奇数,则做ROUND_HALF_UP
        //如果第3位是奇数,则做ROUND_HALF_UP   
 System.out.println("-- ROUND_HALF_EVEN -- 如果第3位是偶数,则做ROUND_HALF_DOWN,如果第3位是奇数, 则做ROUND_HALF_UP --");
        System.out.println("-- ROUND_HALF_EVEN -- 如果第3位是偶数,则做ROUND_HALF_DOWN,如果第3位是奇数, 则做ROUND_HALF_UP --");
 System.out.println(round(12.346, 2, BigDecimal.ROUND_HALF_EVEN));//12.35
        System.out.println(round(12.346, 2, BigDecimal.ROUND_HALF_EVEN));//12.35   
 System.out.println(round(12.345, 2, BigDecimal.ROUND_HALF_EVEN));//12.35
        System.out.println(round(12.345, 2, BigDecimal.ROUND_HALF_EVEN));//12.35   
 }
    }
 }
}

用于较小数的 BigDecimal
从 JDK 1.3 起,Java 开发人员就有了另一种数值表示法来表示非整数: BigDecimal 。 BigDecimal 是标准的类,在编译器中不需要特殊支持,它可以表示任意精度的小数,并对它们进行计算。在内部,可以用任意精度任何范围的值和一个换算因子来表示 BigDecimal ,换算因子表示左移小数点多少位,从而得到所期望范围内的值。因此,用 BigDecimal 表示的数的形式为 unscaledValue*10 -scale 。 
用于加、减、乘和除的方法给 BigDecimal 值提供了算术运算。由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的 BigDecimal 对象。因此,因为创建对象的开销, BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。如果您正在寻找一种能精确表示如货币量这样的数值,则 BigDecimal 可以很好地胜任该任务。 
所有的 equals 方法都不能真正测试相等
如浮点类型一样, BigDecimal 也有一些令人奇怪的行为。尤其在使用 equals() 方法来检测数值之间是否相等时要小心。 equals() 方法认为,两个表示同一个数但换算值不同(例如, 100.00 和 100.000 )的 BigDecimal 值是不相等的。然而, compareTo() 方法会认为这两个数是相等的,所以在从数值上比较两个 BigDecimal 值时,应该使用 compareTo() 而不是 equals() 。 
另外还有一些情形,任意精度的小数运算仍不能表示精确结果。例如, 1 除以 9 会产生无限循环的小数 .111111... 。出于这个原因,在进行除法运算时, BigDecimal 可以让您显式地控制舍入。 movePointLeft() 方法支持 10 的幂次方的精确除法。 
使用 BigDecimal 作为互换类型
SQL-92 包括 DECIMAL 数据类型,它是用于表示定点小数的精确数字类型,它可以对小数进行基本的算术运算。一些 SQL 语言喜欢称此类型为 NUMERIC 类型,其它一些 SQL 语言则引入了 MONEY 数据类型,MONEY 数据类型被定义为小数点右侧带有两位的小数。 
如果希望将数字存储到数据库中的 DECIMAL 字段,或从 DECIMAL 字段检索值,则如何确保精确地转换该数字?您可能不希望使用由 JDBC PreparedStatement 和 ResultSet 类所提供的 setFloat() 和 getFloat() 方法,因为浮点数与小数之间的转换可能会丧失精确性。相反,请使用 PreparedStatement 和 ResultSet 的 setBigDecimal() 及 getBigDecimal() 方法。 
对于 BigDecimal ,有几个可用的构造函数。其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的 String 表示作为输入。要小心使用 BigDecimal(double) 构造函数,因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或 String 的构造函数。 
构造 BigDecimal 数
对于 BigDecimal ,有几个可用的构造函数。其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的 String 表示作为输入。要小心使用 BigDecimal(double) 构造函数,因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或 String 的构造函数。 
如果使用 BigDecimal(double) 构造函数不恰当,在传递给 JDBC setBigDecimal() 方法时,会造成似乎很奇怪的 JDBC 驱动程序中的异常。例如,考虑以下 JDBC 代码,该代码希望将数字 0.01 存储到小数字段: 
    
        
            |  PreparedStatement ps =
            connection.prepareStatement("INSERT INTO Foo SET name=?, value=?");
            ps.setString(1, "penny");
            ps.setBigDecimal(2, new BigDecimal(0.01));
            ps.executeUpdate();
             | 
    
在执行这段似乎无害的代码时会抛出一些令人迷惑不解的异常(这取决于具体的 JDBC 驱动程序),因为 0.01 的双精度近似值会导致大的换算值,这可能会使 JDBC 驱动程序或数据库感到迷惑。JDBC 驱动程序会产生异常,但可能不会说明代码实际上错在哪里,除非意识到二进制浮点数的局限性。相反,使用 BigDecimal("0.01") 或 BigDecimal(1, 2) 构造 BigDecimal 来避免这类问题,因为这两种方法都可以精确地表示小数。 
本文参考了一下文章,对这些作者表示感谢!
 
                                                                 THE END 
	posted on 2010-02-22 13:59 
小立飞刀 阅读(5531) 
评论(0)  编辑  收藏  所属分类: 
Others