首页 > 其他分享 >BigDecimal使用问题

BigDecimal使用问题

时间:2023-04-04 21:23:39浏览次数:25  
标签:scale BigDecimal param 问题 v1 v2 使用 new

Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。

一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以开发中,如果我们需要精确计算的结果,则必须使用BigDecimal类来操作。

BigDecimal所创建的是对象,故我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

  1. 在需要精确的小数计算时再使用BigDecimal,BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。 尽量使用参数类型为String的构造函数。
  2. BigDecimal都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。

问题:

使用double和float的构造方法构造BigDecimal时,精度丢失:

将 double和float 转换成字符串之后再构造BigDecimal,就不会出现这种问题: Double.toString(double)

BigDecimal a =new BigDecimal(0.1);
System.out.println("a values is:"+a);
System.out.println("=====================");
BigDecimal b =new BigDecimal("0.1");
System.out.println("b values is:"+b);

a values is:0.1000000000000000055511151231257827021181583404541015625
=====================
b values is:0.1

原因分析:
1)参数类型为double的构造方法的结果有一定的不可预知性。有人可能认为在Java中写入newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于0.1000000000000000055511151231257827021181583404541015625。这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。
2)String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言, 通常建议优先使用String构造方法。
3)当double必须用作BigDecimal的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用Double.toString(double)方法,然后使用BigDecimal(String)构造方法,将double转换为String。要获取该结果,请使用static valueOf(double)方法。

除法的时候出现异常

通过BigDecimal的divide方法进行除法时当不整除,出现无限循环小数时,就会抛异常:
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result
解决方法:
divide方法设置精确的小数点,如:divide(xxxxx,2)

BigDecimal格式化:

由于NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制。
以利用BigDecimal对货币和百分比格式化为例。首先,创建BigDecimal对象,进行BigDecimal的算术运算后,分别建立对货币和百分比格式化的引用,最后利用BigDecimal对象作为format()方法的参数,输出其格式化的货币值和百分比。

NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用
NumberFormat percent = NumberFormat.getPercentInstance();  //建立百分比格式化引用
percent.setMaximumFractionDigits(3); //百分比小数点最多3位

BigDecimal loanAmount = new BigDecimal("15000.48"); //贷款金额
BigDecimal interestRate = new BigDecimal("0.008"); //利率
BigDecimal interest = loanAmount.multiply(interestRate); //相乘

System.out.println("贷款金额:\t" + currency.format(loanAmount));
System.out.println("利率:\t" + percent.format(interestRate));
System.out.println("利息:\t" + currency.format(interest));

贷款金额: ¥15,000.48 利率: 0.8% 利息: ¥120.00

BigDecimal格式化保留2为小数,不足则补0:
public class NumberFormat {

    public static void main(String[] s){
        System.out.println(formatToNumber(new BigDecimal("3.435")));
        System.out.println(formatToNumber(new BigDecimal(0)));
        System.out.println(formatToNumber(new BigDecimal("0.00")));
        System.out.println(formatToNumber(new BigDecimal("0.001")));
        System.out.println(formatToNumber(new BigDecimal("0.006")));
        System.out.println(formatToNumber(new BigDecimal("0.206")));
    }
    /**
     * @desc 1.0~1之间的BigDecimal小数,格式化后失去前面的0,则前面直接加上0。
     * 2.传入的参数等于0,则直接返回字符串"0.00"
     * 3.大于1的小数,直接格式化返回字符串
     * @param obj传入的小数
     * @return
     */
    public static String formatToNumber(BigDecimal obj) {
        DecimalFormat df = new DecimalFormat("#.00");
        if(obj.compareTo(BigDecimal.ZERO)==0) {
            return "0.00";
        }else if(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(new BigDecimal(1))<0){
            return "0"+df.format(obj).toString();
        }else {
            return df.format(obj).toString();
        }
    }
}
结果为:
3.44
0.00
0.00
0.00
0.01
0.21

工具类推荐:

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;
    }
    }

标签:scale,BigDecimal,param,问题,v1,v2,使用,new
From: https://www.cnblogs.com/jimmyhu/p/17287945.html

相关文章

  • 解决Abp设置DefaultLanguage默认语言不生效的问题
    @目录现象原因分析解决问题现象默认地,Abp的语言提供程序将返回的CultureInfo为En,在一些默认实现的接口(比如/api/TokenAuth/Authenticate)返回的错误信息是英文目标是改成简体中文显示,但是即便我们在AbpSettings表中加入了DefaultLanguage为"zh-Hans"AddSettingIfNotExists(L......
  • 解决arcmap10.2无法连接arcgis server管理的问题
    最近重装了一台电脑,安装了常用的arcgis10.2系列软件,安装过程比较顺利。可是安装后arcmap却无法连接server,在下面界面提交完成,报用户名密码错误: 解决办法:(1)打开manager,在安全性--角色处新建角色administrator (2)在用户处,新建用户agsserver,设置密码,设置角色为administrator......
  • 使用Android NDK Camera2经验总结
    2023年03月30日NDKCamera参考文章:https://blog.csdn.net/daihuimaozideren/article/details/101235393项目链接:https://github.com/qi-xmu/Android-ndk-camera-zh.git项目基于官方NDKCamera2texturesample,添加了详细的中午注释,点个Star!!!谢谢!第一部分程序入口逻辑首先需......
  • 小程序 使用navigateBack返回携带参数
    在写业务的时候,我们有时候会遇到这样的场景:从a页面跳转到b页面,然后需要再从b页面返回到a页面;并携带一些数据回来。这个时候通常我们会使用到  wx.navigateBack但是由于navigateBack无法直接携带参数;传参就需要另想办法了当然实现的方法有很多;这里要说的是通过setData的方......
  • 【flask】蓝图的使用方式 g对象的使用 flask配置数据库连接池
    目录上节回顾今日内容1蓝图的使用2g对象g对象vsrequest对象3数据库连接池上节回顾全局request对象。线程会处理请求,确保线程中的数据不错乱。django_session表:#1cbv使用 -写一个类继承MethodView,写get,post。。。-类属性decorators=[auth,]可以加装饰器......
  • Dajngo使用xadmin2.0遇到的问题
    使用xadmin遇到的问题&解决前置:注意:这里只写出我使用时发现的问题关联的包django>=2.0django-crispy-forms>=1.6.0django-import-export>=0.5.1django-reversion>=2.0.0django-formtools==2.1future==0.15.2httplib2==0.9.2six==1.10.0下载链接:htt......
  • 设置Visual Studio以使用虚幻引擎的提示、技巧和技术
    设置VisualStudio设置VisualStudio以使用虚幻引擎的提示、技巧和技术Beginner本页面的内容版本兼容性新安装VisualStudio时的选项推荐设置虚幻引擎(简称UE) 能与 VisualStudio(简称VS) 完美结合,使你能够快速、简单地改写项目代码,并能即刻查......
  • JVM——JVM级别下的高并发问题
    摘要本文将深入的学习与分析JVM虚拟机的原理和相关的调优的相关实例。JVM级别下的高并发问题Java内存模型与线程“让计算机并发执行若干个运算任务”与“更充分地利用计算机处理器的效能”之间的因果关系,看起来顺理成章,实际上它们之间的关系并没有想象中的那么简单,其中一个重......
  • 算法问题——动态规划和回溯算法问题
    回溯算法树形问题排列问题组合问题二位平面的回溯算法回溯递归问题树形问题17.电话号码的字母组合(全排列的问题)/***Copyright(C),2018-2020*FileName:letterCombinations*Author:xjl*Date:2020/3/2015:30*Description:给定一个仅包含数字2-9的字......
  • Redis——面试问题集合
    那你能说说Redis是单线程的?Redis完全基于内存,绝大部分请求是纯粹的内存操作,非常迅速,数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度是O(1)。数据结构简单,对数据操作也简单。采用单线程,避免了不必要的上下文切换和竞争条件,不存在多线程导致的CPU切换......