java中浮点数精度的问题与BigDecimal

出现的问题:

一般来说,高字节数往低字节数转换时会出现精度丢失的问题,而低字节数往高字节转换时不会出现精度丢失的问题

但是也有例外:

@Test
    public void test() {
        float f = 12.12325f;
        double d = f;
        System.out.println(d);
    }

预期结果:12.12325

实际结果:12.123250007629395

结论:出现了精度丢失

原因:

对于这个现象,我百度,goole之后都没有给出一个比较详细的解释,有哪位知道麻烦告诉我啊,谢谢。

解决方法:

先将float类型的数据转换为String类型,在转换为double类型

@Test
    public void test() {
        float f = 12.12325f;
        double d = f;
        System.out.println(Double.valueOf(String.valueOf(f)));
    }

运算结果:12.12325

BigDecimal:

再查找上面问题时发现了 BigDecimal

百度百科的解释:

Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。 [1] 

从上面知道 BigDecimal 是比 float 和 double 表示范围更大的一种数据类型

示例:

@Test
    public void test5(){
        float f = 0.11F;
        double d = 4324323.12;
        System.out.println(d+f);
    }

预期结果:4324323.23

实际结果:4324323.2299999995

解决方法一(未解决问题):

使用 BigDecimal 的 BigDecimal(double val)和 BigDecimal(float val)方法

@Test
    public void test6(){
        float f = 0.11F;
        double d = 4324323.12;

        BigDecimal bd1 = new BigDecimal(f);
        BigDecimal bd2 = new BigDecimal(d);

        System.out.println(bd1.add(bd2));
    }

运算结果:4324323.229999999515712261199951171875

发现并没有解决问题,仍然出现了精度丢失的问题

解决方法二:

使用 BigDecimal 的 BigDecimal(String val)方法

@Test
    public void test4(){
        float f = 0.11F;
        double d = 4324323.12;

        BigDecimal bd1 = new BigDecimal(String.valueOf(f));
        BigDecimal bd2 = new BigDecimal(String.valueOf(d));

        System.out.println(bd1.add(bd2));
    }

运算结果:4324323.23

发现这次没有出现丢失精度的问题

 

Utils:

如果每次进行浮点数运算时都将浮点数先转为String类型,在进行运算,会比较繁琐,这里有大神写的工具类可以直接拿来用:

package com.util;
import java.math.BigDecimal;
 
/**
 * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精确的浮点数运算,包括加減乘除和四捨五入。
 */
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 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 mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }
 
    /**
     * 提供(相对)精确的除非运算,当发生除不尽的情况时,精确到小数点以后10位,以后的数字四舍五入
     * @param v1
     *            被除數
     * @param v2
     *            除數
     * @return 兩個參數的商
     */
    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }
 
    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入
     * @param v1
     *            被除數
     * @param v2
     *            除數
     * @param scale
     *            表示表示需要精確到小數點以後位数。
     * @return 兩個參數的商
     */
    public static double div(double v1, double v2, int scale) {
        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, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
 
    /**
     * 提供精確的小數位四捨五入處理。
     * 提供精确的小数位四舍五入处理
     *
     * @param v
     *            需要四捨五入的數位
     * @param scale
     *            小數點後保留幾位
     * @return 四捨五入後的結果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
}

本人才疏学浅,有写的不对或不到的地方希望各位指出

猜你喜欢

转载自blog.csdn.net/qq_33371372/article/details/81350065