java.math BigDecimal类

双精度浮点型变量double可以处理16位有效数,但是超过16位后呢,要用什么来表示呢?

double在做算术运算时,会出现一定的偏差,如果在一般的情况下使用倒是可以, 但如果在商业领域,如:银行业务利息计算,商场交易等。 可能会出现不好处理的问题。

System.out.println(2- 1.1);// 结果: 0.8999999999999999

其实,我们在表示一个double的值的时候,都是不完整的, 才会导致2- 1.1 不等于0.9。

那么? 如何解决这样的需要高度精确度的数字计算呢?

这里,Java提供了一个类BigDecimal。

BigDecimal类是不可变的、任意精度的有符号十进制数对象。(精度一般指的都是小数点后面的位数

BigDecimal由任意精度的整数非标度值 和 32 位的整数标度 (scale) 组成。(可以看他的valueof()方法)

如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负 scale 次幂。

(其实在试验中,非标度值也可以是负数)

BigDecimal提供以下操作:算术、标度操作、舍入、比较、哈希算法和格式转换。

toString()方法提供 BigDecimal 的规范表示形式。

*****文章的最后提供了八中舍入方式的详细介绍*****

下面是各种示例:

package javamath;

import java.math.BigDecimal;
import java.math.MathContext;
import java.text.NumberFormat;

/**
 * Created by Administrator on 2018/3/13.
 */
public class BigDecimalDemo {

    //1 没有无参构造方法, 如果没有参数,会直接编译错误。


    // 构造方法
    public static void testCon(double d1){

        BigDecimal b1 = new BigDecimal(d1);
        System.out.println("double参数的构造方法"+b1);

        //d1=2.01 的结果 :2.00 9999 9999 9999 9786 8371 7927 1969 9442 3866 271 9726 5625
        //显然存在一定的误差
        //所以这个方法不建议使用, 因为结果具有不可预知性,  具体原因可以参照文档解释。
    }

    // 上面可修改为
    public static void testCon2(double d1){
        BigDecimal b1 = new BigDecimal(Double.toString(d1));
        System.out.println(b1);
    }
    // 最好使用,通过String作为参数来构造
    public static void testString(){
        BigDecimal b1 = new BigDecimal("2.111");
        System.out.println(b1);
    }

    //静态工厂方法, 建议使用这个方法创建对象。内部存放着一些固定的对象, 可以直接使用
    //两个参数都可以为0 ,
    public static void testValueof(long unscaledVal, int scale){
        BigDecimal b1 = BigDecimal.valueOf(unscaledVal,scale);
        System.out.println(b1);
    }

    // BigDecimal 转化为基本数据类型
    //转化都会导致数据丢失,不建议使用。
    public static void testXXXValue(){
        BigDecimal b1 = new BigDecimal("89.1234567890123456789");
        //转化为双精度,有效位是16位。
        double d1 = b1.doubleValue();
        System.out.println("双精度:"+d1);  //双精度:89123.45678901234    刚好16位

        // 说明这样的转化, 丢失了数据的精度(准确度),另外其他的XXXXValue() 方法也是如此。 要慎重使用。
        // 那么问题来了 : 如何创建一个超过16位有效位数的数字呢? 我们都知道double 也才只有16位有效位数
        // 肯定不能作为BigDecimal参数的,
        // 所以这里只能用String类型的参数了。

    }

    //精准加法运算
    public static void testAdd(){
        BigDecimal b1 = new BigDecimal("22.22");
        BigDecimal b2 = new BigDecimal("22.2");
        b1 = b1.add(b2);
        System.out.println("加法:"+b1);   //加法:44.42 ,   说明: 标度值 Max(b1.scale,b2.cale) 去最大的那个。
    }

    // 精准减法
    public static void testSubtract(){
        BigDecimal b1 = new BigDecimal("22.22");
        BigDecimal b2 = new BigDecimal("22.2");
        b1 = b1.subtract(b2);
        System.out.println(b1);   //0.02   说明: 标度值 Max(b1.scale,b2.cale) 去最大的那个。
    }


    // 乘法运算
    public static void testmultiply(){
        BigDecimal b1 = new BigDecimal("22.22");
        BigDecimal b2 = new BigDecimal("0.02");
        b1 = b1.multiply(b2);
        System.out.println(b1); //0.4444   说明: 标度值scale = b1.scale+ b2.scale+ .....

    }
    //除法
    public static void testDivide(){
        BigDecimal b1 = new BigDecimal(1);
        BigDecimal b2 = new BigDecimal(3);
//        b1 = b1.divide(b2);        // 1/3当无法除尽时,会报错 ,因为没有提供舍入模式,和标度值
        System.out.println(b1);
    }
    
    //精准除法
    public static void testDivide2(){
        BigDecimal b1 = new BigDecimal(1);
        BigDecimal b2 = new BigDecimal(3);
        b1=b1.divide(b2,8,BigDecimal.ROUND_CEILING);  //向正无限大方向舍入的舍入模式, 标度为8
        System.out.println(b1);         //0.33333334
    }

    //格式化
    //由于NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,
    //可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制
    // 货币格式, 百分比
    public static void testFormat(){
        BigDecimal b1 = new BigDecimal("12345678901244442.46666");
        BigDecimal b2 = new BigDecimal(100);
        BigDecimal b1In = b1.multiply(b2);
        //货币格式化引用
        NumberFormat  c = NumberFormat.getCurrencyInstance();
        //建立百分比引用
        NumberFormat p = NumberFormat.getPercentInstance();
        p.setMaximumFractionDigits(3);              //  设置百分比小数点最多3位数

        System.out.println("b1In:"+b1In);
        System.out.println("货币格式:"+c.format(b1));   //¥12,345,678,901,244,442.47 多次测试后小数点后面固定两位。

        System.out.println("百分比格式:"+p.format(b1));


    }


    public static void main(String[] args) {

//        testSubtract();
        testValueof(-1,1);
    }


}

舍入模式 : 这个只在除法运算中才会用到。

Java提供了一个枚举类来存放这些舍入模式。RoundingMode 类。里面写的很详细了。想看的时候点进去看看。







猜你喜欢

转载自blog.csdn.net/qq_41694349/article/details/79538348