• Java 大数值类型执行精确计算


    简介

    如果基本的整数和浮点数精度不能够满足需求,那么可以使用 java.math 包下两个很有用的类:BigInteger 和 BigDecimal。这两个类可以处理包含任意长度数字序列的数值,BigInteger 实现了任意精度的整数运算,BigDecimal 实现了任意精度的浮点数运算。BigDecimal 由于舍入模式的存在,使得这个类用起来比 BigInteger 要复杂。

    BigInteger

    Java中long型为最大整数类型,对于超过long型的数据如何去表示呢。在Java的世界中,超过long型的整数已经不能被称为整数了,它们被封装成BigInteger对象。在BigInteger类中,实现四则运算都是方法来实现,并不是采用运算符。

    构造方法

    通过看源码可以知道有6种构造方法,没有无参构造方法,其中比较常用的是第一个。

    public BigInteger(String val) {
        this(val, 10);
    }

    基本运算

    @Test
    public void bigIntegerTest(){
        
        BigInteger big1 = new BigInteger("18");
        BigInteger big2 = new BigInteger("5");
        
        //加法
        BigInteger bigAdd = big1.add(big2);
        System.out.println(bigAdd);
        
        //减法
        BigInteger bigSub = big1.subtract(big2);
        System.out.println(bigSub);
        
        //乘法
        BigInteger bigMul = big1.multiply(big2);
        System.out.println(bigMul);
        
        //除法
        //如果两数相除为小数,直接舍去小数部分,保留整数部分
        BigInteger bigDiv = big1.divide(big2);
        System.out.println(bigDiv);
        
        BigInteger big3 = new BigInteger("-5");
        //绝对值
        BigInteger bigAbs = big3.abs();
        System.out.println(bigAbs);
        
        //次方
        BigInteger bigPow = big3.pow(3);
        System.out.println(bigPow);
        
        //比较,返回两个数中的最大值
        BigInteger bigMax = big1.max(big2);
        System.out.println(bigMax);
        
        //比较,返回两个数中的最小数
        BigInteger bigMin = big1.min(big2);
        System.out.println(bigMin);
        
    }

    BigDecimal

    double和float类型在运算中很容易丢失精度,造成运算结果的不准确,Java提供我们BigDecimal类可以实现浮点数据的高精度运算。

    RoundingMode

    舍入模式:通过源码阅读可知,RoundingMode类中的舍入模式和BigDecimal类中的舍入模式是一样的,选择哪种都行,比如

    RoundingMode.* 
    相当于
    BigDecimal.ROUND_*

    这里以枚举RoundingMode中的舍入模式简单介绍下。这个enum 是打算用来替代 BigDecimal中的舍入模式常量。

    UP

    远离零方向舍入。始终对非零舍弃部分前面的数字加 1。注意,此舍入模式始终不会减少计算值的绝对值。

    DOWN

    向零方向舍入。从不对舍弃部分前面的数字加 1(即截尾)。注意,此舍入模式始终不会增加计算值的绝对值。

    CEILING

    向正无限大方向舍入。如果结果为正,则舍入行为类似于 RoundingMode.UP;如果结果为负,则舍入行为类似于 RoundingMode.DOWN。注意,此舍入模式始终不会减少计算值。

    FLOOR

    向负无限大方向舍入。如果结果为正,则舍入行为类似于 RoundingMode.DOWN;如果结果为负,则舍入行为类似于 RoundingMode.UP。注意,此舍入模式始终不会增加计算值。

    HALF_UP

    向最接近数字方向舍入。如果与两个相邻数字的距离相等,则向上舍入。即:如果被舍弃部分 >= 0.5,则舍入行为同 RoundingMode.UP;否则舍入行为同 RoundingMode.DOWN。

    注意,此舍入模式就是通常学校里讲的四舍五入。

    HALF_DOWN

    向最接近数字方向舍入。如果与两个相邻数字的距离相等,则向下舍入。即:如果被舍弃部分 > 0.5,则舍入行为同 RoundingMode.UP;否则舍入行为同 RoundingMode.DOWN。

    HALF_EVEN

    向最接近数字方向舍入。如果与两个相邻数字的距离相等,则向相邻的偶数舍入。即:如果舍弃部分左边的数字为奇数,则舍入行为同 RoundingMode.HALF_UP;如果为偶数,则舍入行为同 RoundingMode.HALF_DOWN。

    注意,在重复进行一系列计算时,此舍入模式可以在统计上将累加错误减到最小。此舍入模式也称为“银行家舍入法”,主要在美国使用。此舍入模式类似于 Java 中对 float 和 double 算法使用的舍入策略。

    UNNECESSARY

    用于断言的舍入模式,请求的操作具有精确的结果,不需要进行舍入。如果对生成精确结果的操作指定此舍入模式,则抛出 ArithmeticException。

    MathContext

    RoundingMode 是舍入模式的抽象描述,仅仅描述了舍入的规则,但是运算中还有一些其他的规则,比如:保留几位有效数字?

    MathContext 则是针对于计算的更进一步抽象,是封装上下文设置的不可变对象,它描述数字运算符的某些规则。拥有两个属性。

    构造方法

    构造一个新的 MathContext,它具有指定的精度和舍入模式

    MathContext(int setPrecision, RoundingMode setRoundingMode)

    构造一个新的 MathContext,它具有指定的精度和 HALF_UP 舍入模式

    MathContext(int setPrecision)
    根据字符串构造一个新的 MathContext

    注意:该字符串的格式必须与 toString() 方法生成的字符串的格式相同,不可以随便写!

    MathContext(String val)
    public java.lang.String toString() {
        return "precision=" + precision + " " +
               "roundingMode=" + roundingMode.toString();
    }

    静态对象

    MathContext 内置了几个静态对象供我们使用

    UNLIMITED

    其设置具有无限精度算法所需值的 MathContext 对象

    public static final MathContext UNLIMITED = new MathContext(0, RoundingMode.HALF_UP);

    DECIMAL32

    其精度设置与 IEEE 754R Decimal32 格式(即 7 个数字)匹配,舍入模式为 HALF_EVEN,这是 IEEE 754R 的默认舍入模式

    public static final MathContext DECIMAL32 = new MathContext(7, RoundingMode.HALF_EVEN);

    DECIMAL64

    其精度设置与 IEEE 754R Decimal64 格式(即 16 个数字)匹配,舍入模式为 HALF_EVEN,这是 IEEE 754R 的默认舍入模式

    public static final MathContext DECIMAL64 = new MathContext(16, RoundingMode.HALF_EVEN);

    DECIMAL128

    其精度设置与 IEEE 754R Decimal128 格式(即 34 个数字)匹配,舍入模式为 HALF_EVEN,这是 IEEE 754R 的默认舍入模式

    public static final MathContext DECIMAL128 = new MathContext(34, RoundingMode.HALF_EVEN);

    示例代码

    @Test
    public void bigDecimalTest(){
        
        BigDecimal big1 = new BigDecimal("10.55");
        BigDecimal big2 = new BigDecimal("0.56");
        
        //指定精度和舍入模式
        //四舍五入保留三个数字
        MathContext mathContext = new MathContext(3, RoundingMode.HALF_UP);
        
        //加法
        BigDecimal bigAdd = big1.add(big2);
        System.out.println(bigAdd);//11.11
        BigDecimal bigAdd1 = big1.add(big2, mathContext);
        System.out.println(bigAdd1);//11.1
        
        //减法
        BigDecimal bigSub = big1.subtract(big2);
        System.out.println(bigSub);//9.99
        BigDecimal bigSub1 = big1.subtract(big2, mathContext);
        System.out.println(bigSub1);//9.99
        
        //乘法
        BigDecimal bigMul = big1.multiply(big2);
        System.out.println(bigMul);//5.9080
        BigDecimal bigMul1 = big1.multiply(big2, mathContext);
        System.out.println(bigMul1);//5.91
        
        //除法
        //注意如果除不尽,则会出现java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
        //BigDecimal bigDiv = big1.divide(big2);
        //System.out.println(bigDiv);
        //指定保留数字位数和舍入模式
        BigDecimal bigDiv1 = big1.divide(big2, mathContext);
        System.out.println(bigDiv1);//18.8
        //指定保留小数位数和舍入模式
        BigDecimal bigDiv2 = big1.divide(big2, 2, RoundingMode.HALF_UP);
        System.out.println(bigDiv2);//18.84
        
    }

    注意事项

    参考博客:https://blog.csdn.net/ugg/article/details/8213666

    1、尽量避免传递double类型,有可能话,尽量使用int和String类型。

    直接传double类型的话,比如浮点型0.6,放入BigDecimal中就成了

    0.59999999999999997779553950749686919152736663818359375,从而会造成计算结果错误。

    2、做乘除计算时,一定要设置精度和保留小数点位数。

    不设置的话,如果结果是一个无限循环小数的话,会抛出异常。

    java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

    3、BigDecimal计算时,单独放到try catch内。

    4、MathContext 中的 precision 是保留数字的位数, BigDecimal 中的 scale 保留小数的位数。是不一样的。

  • 相关阅读:
    微信浏览器 video
    css 日常
    input file 上传文件类型控制
    JS的一些日常
    使用canvas时, 如何用相对单位(rem, rpx)来适配不同机型
    微信小程序 textarea的placeholder层级过高 在弹层之上 bug解决方法
    保留两位小数, 不足自动补零
    Java创建线程的两个方法
    android socket 网络数据传输
    java中InputStream中read()与read(byte[] b) 用法介绍
  • 原文地址:https://www.cnblogs.com/wbxk/p/6910987.html
Copyright © 2020-2023  润新知