首页 > 编程语言 >JAVA之BigDecimal详解

JAVA之BigDecimal详解

时间:2024-02-05 16:12:24浏览次数:32  
标签:scale JAVA BigDecimal setScale v1 详解 new ROUND

一、BigDecimal概述

Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以开发中,如果我们需要精确计算的结果,则必须使用BigDecimal类来操作。

二、BigDecimal常用方法

2.1常用方法

  • add(BigDecimal)
    BigDecimal对象中的值相加,返回BigDecimal对象
  • subtract(BigDecimal)
    BigDecimal对象中的值相减,返回BigDecimal对象
  • multiply(BigDecimal)
    BigDecimal对象中的值相乘,返回BigDecimal对象
  • divide(BigDecimal)
    BigDecimal对象中的值相除,返回BigDecimal对象
  • toString()
    将BigDecimal对象中的值转换成字符串
  • doubleValue()
    将BigDecimal对象中的值转换成双精度数
  • floatValue()
    将BigDecimal对象中的值转换成单精度数
  • longValue()
    将BigDecimal对象中的值转换成长整数
  • intValue()
    将BigDecimal对象中的值转换成整数

2.2compareTo()方法详解

java.math.BigDecimal.compareTo(BigDecimal bg)方法用来此BigDecimal和作为参数传递的BigDecimal对象是否相等。

返回值

  • -1:此BigDecimal对象小于作为参数传递的BigDecimal对象
  • 0:此BigDecimal对象等于作为参数传递的BigDecimal对象
  • 1:此BigDecimal对象大于作为参数传递的BigDecimal对象

注意:compareTo方法比较两个BigDecimal对象时,只比较两个对象的值,不考虑精度;而equals方法不仅比较两个对象的值,还会考虑精度。

BigDecimal b1, b2;
b1 = new BigDecimal(67891);
b2 = new BigDecimal("67891.000");
b1.compareTo(b2)	//返回值为0,两者相等
b1.equals(b2)	//返回值false,两者不相等

2.3BigDecimal舍入模式详解
-ROUND_UP

进位制:不管保留数字后面是大是小 (0 除外) 都会进 1。结果会向原点的反方向对齐,正数向正无穷方向对齐,负数向负无穷方向对齐。

BigDecimal a = new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_UP); // 0.10
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_UP); // 0.10
BigDecimal c = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_UP); // -0.10
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_UP); // -0.10
BigDecimal e = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_UP); // -0.09
BigDecimal f = new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_UP); // 0.09

-ROUND_DOWN

舍去制,截断操作,后面所有数字直接去除。结果会向原点方向对齐。

BigDecimal a = new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_DOWN); // 0.09
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_DOWN); // 0.09
BigDecimal c = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_DOWN); // -0.09
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_DOWN); // -0.09
BigDecimal e = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_DOWN); // -0.09
BigDecimal f = new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_DOWN); // 0.09

-ROUND_HALF_UP

根据保留数字后一位 >=5 进行四舍五入。如果舍弃部分的最高位大于等于 5,则进1,向原点反方向对齐,否则舍去,向原点方向对齐。

BigDecimal a = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_UP); // 0.10
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_HALF_UP); // 0.09
BigDecimal c = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.10
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.09
BigDecimal e = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.09
BigDecimal f = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.10

-ROUND_HALF_DOWN

根据保留数字后一位 >5 进行五舍六入。如果舍弃部分的最高位大于 5,则进1,向原点反方向对齐,否则舍去,向原点方向对齐。这种模式也就是我们常说的 “五舍六入”。

BigDecimal a = new BigDecimal("0.096").setScale(2, BigDecimal.ROUND_HALF_DOWN); // 0.10
BigDecimal b = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_DOWN); // 0.09
BigDecimal c = new BigDecimal("-0.096").setScale(2, BigDecimal.ROUND_HALF_DOWN); // -0.10
BigDecimal d = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_DOWN); // -0.09
BigDecimal e = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_HALF_DOWN); // -0.09

-ROUND_CEILING

向正无穷方向对齐,转换为正无穷方向最接近的数值。如果为正数,行为和 ROUND_UP 一样;如果为负数,行为和 ROUND_DOWN 一样。

BigDecimal a = new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_CEILING); // 0.10
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_CEILING); // 0.10
BigDecimal c = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_CEILING); // -0.09
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_CEILING); // -0.09
BigDecimal e = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_CEILING); // -0.09
BigDecimal f = new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_CEILING); // 0.09

-ROUND_FLOOR

向负无穷方向对齐。如果为正数,行为和 ROUND_DOWN 一样;如果为负数,行为和 ROUND_UP 一样。

BigDecimal a = new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_FLOOR); // 0.09
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_FLOOR); // 0.09
BigDecimal c = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_FLOOR); // -0.10
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_FLOOR); // -0.10
BigDecimal e = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_FLOOR); // -0.09
BigDecimal f = new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_FLOOR); // 0.09

-ROUND_HALF_EVEN

四舍六入五成双,如果舍弃部分的最高位大于等于六,或等于五并且前一位是奇数,则进1,除了这两者情况外都舍去。可以理解为如果舍弃部分左边的数字为奇数,则作 ROUND_HALF_UP;如果为偶数,则作ROUND_HALF_DOWN。

BigDecimal a = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_EVEN); // 0.10
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_HALF_EVEN); // 0.09
BigDecimal c = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.10
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.09
BigDecimal e = new BigDecimal("-0.085").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.08
BigDecimal f = new BigDecimal("-0.084").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.08
BigDecimal g = new BigDecimal("-0.086").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.09

三、总结

  1. 参数类型为double的构造方法的结果有一定的不可预知性。通常建议优先使用String构造方法,或者通过BigDecimal.valueOf()来构建对象
  2. compareTo方法比较两个BigDecimal对象时,只比较两个对象的值,不考虑精度;而equals方法不仅比较两个对象的值,还会考虑精度。
  3. BigDecimal一旦创建都是不可变的的, 每次进行四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得创建新的BigDecimal对象来保存操作后的值。

四、BigDecimal 工具类分享

BigDecimal 工具类,提供了多个静态方法来简化 BigDecimal 的操作。

package com.vivo.ars.util;
import java.math.BigDecimal;

/**
 * 用于高精确处理常用的数学运算
 */
public class ArithmeticUtils {
    //默认除法运算精度
    private static final int DEF_DIV_SCALE = 10;

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

    /**
     * 提供精确的加法运算
     *
     * @param v1    被加数
     * @param v2    加数
     * @param scale 保留scale 位小数
     * @return 两个参数的和
     */
    public static String add(String v1, String v2, int scale) {
        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.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

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

    /**
     * 提供精确的减法运算
     *
     * @param v1    被减数
     * @param v2    减数
     * @param scale 保留scale 位小数
     * @return 两个参数的差
     */
    public static String sub(String v1, String v2, int scale) {
        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.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

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

    /**
     * 提供精确的乘法运算
     *
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     */
    public static double mul(double v1, double v2, int scale) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return round(b1.multiply(b2).doubleValue(), scale);
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     */
    public static String mul(String v1, String v2, int scale) {
        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.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
     * 小数点以后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();
    }

    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
     * 定精度,以后的数字四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示需要精确到小数点以后几位
     * @return 两个参数的商
     */
    public static String div(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v1);
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供精确的小数位四舍五入处理
     *
     * @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));
        return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static String round(String v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(v);
        return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 取余数
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     */
    public static String remainder(String v1, String v2, int scale) {
        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.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 取余数  BigDecimal
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     */
    public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 比较大小
     *
     * @param v1 被比较数
     * @param v2 比较数
     * @return 如果v1 大于v2 则 返回true 否则false
     */
    public static boolean compare(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        int bj = b1.compareTo(b2);
        boolean res;
        if (bj > 0)
            res = true;
        else
            res = false;
        return res;
    }
}

参考资料:华仔Coding的博客
CSDN-长安明月
https://vimsky.com/examples/usage/bigdecimal-compareto-function-in-java.html

标签:scale,JAVA,BigDecimal,setScale,v1,详解,new,ROUND
From: https://www.cnblogs.com/Aying216/p/17997249

相关文章

  • Java-08常用容器
    List接口:java.util.List<>。实现:java.util.ArrayList<>:变长数组java.util.LinkedList<>:双链表函数:add():在末尾添加一个元素clear():清空size():返回长度isEmpty():是否为空get(i):获取第i个元素set(i,val):将第i个元素设置为val栈类:java.util.Stack<>函......
  • Java-09异常处理
    tip:[start]异常处理可以允许我们在程序运行时进行诊断和补救。——闫学灿tip:[end]Error与Exception的区别Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。此类异常是程序的致命异常,是无法捕获处理的。......
  • 基于BiLSTM-CRF模型的分词、词性标注、信息抽取任务的详解,侧重模型推导细化以及LAC分
    基于BiLSTM-CRF模型的分词、词性标注、信息抽取任务的详解,侧重模型推导细化以及LAC分词实践1.GRU简介GRU(GateRecurrentUnit)门控循环单元,是[循环神经网络](RNN)的变种种,与LSTM类似通过门控单元解决RNN中不能长期记忆和反向传播中的梯度等问题。与LSTM相比,GRU内部的网络架......
  • Kubernetes 服务类型详解
    Kubernetes服务类型详解如今,Kubernetes已成为管理和扩展云原生应用程序的强大工具。组织需要利用高度可扩展且始终可用的功能来保持零停机时间,快速部署他们的软件。随着越来越多的应用程序被容器化和部署,容器的管理也变得越来越复杂。因此,软件的扩展成为一个问题,而这正是Kuber......
  • abc339 详解
    第一篇整场题解纪念我第一次AK的abc!A从后往前找到第一个'.'然后输出'.'到字符串结尾构成的字符串。#include<iostream>usingnamespacestd;intmain(intargc,constchar*argv[]){stringstr;cin>>str;intlen=(int)str.length();stri......
  • 淘宝/天猫商品详情API:返回值参数详解及商业逻辑实现
    在电子商务的高速发展过程中,API接口扮演了至关重要的角色。对于淘宝和天猫这样的大型电商平台,商品详情API是商家与消费者信息沟通的桥梁。本文将深入探讨这一API的返回值参数,并展示如何通过编程利用这些数据实现商业逻辑。一、商品详情API的核心作用商品详情页是电商体验中的重要环......
  • RocketMQ_详细配置与使用详解
    为什么要用MQ 应用解耦系统的耦合性越高,容错性就越低。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。 使用消息队列解耦合,系统的耦合性就会提高了。......
  • 《视频篇》java实现下载hls(m3u8+ts)实时流并进行合并mp4
    链接:https://blog.csdn.net/qq_41604890/article/details/130143355首先需要了解什么是HLS简单理解就是,m3u8文件存放着可供客户端播放TS片段简单一点说m3u8加密技术就是将原视频分割成n个.ts文件,并用一个key文件对每一个.ts文件加密,其中m3u8文件里面存储了key文件和所有的.t......
  • 深入浅出Java多线程(十):CAS
    引言大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第十篇内容:CAS。大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!!在多线程编程中,对共享资源的安全访问和同步控制是至关重要的。传统的锁机制,如synchronized关键字和ReentrantLock等,能够有效防止多个线程同时......
  • 深入浅出Java多线程(八):volatile
    引言大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第八篇内容:volatile。大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!!在当今的软件开发领域,多线程编程已经成为提高系统性能和响应速度的重要手段。Java作为广泛应用的多线程支持语言,其内存模型(JMM)设计......