java使用BigDecimal 处理商业精度及高精度详解

java存大数和高精度浮点数(BigInteger与BigDecimal) BigInteger与BigDecimal BigInteger 背景: Integer 类作为 int 的包装类,能存储的最大的整数值为 2^31 - 1, Long类也是有限的,最大为 2^63 - 1. 如果要表示再大的整数,不管是基本数据类型还是他们的包装类都无能为力,更不用说进行运算了。 java.math包的BigInteger 可以表示不可变的任意精度的整数。BigInteger 提供所有 Java的基本整数操作符的对应物,并提供 java.lang.Math的所有方法相关。另外,BigInt 阅读详情

前言

之前我是写过一篇类似笔记:

java处理高精度的商业计算

但是呢,写的太简单,关键还没有写到要点,所以重新写一篇。


情形

由于公司最近要求把股票相关的数据,全部交给后端来处理,不再由前端来处理。 
股票大家都知道,这里面的计算都是商业级别的,小数点4+位那是再正常不过啦。 
比如这样几组数字

2539230979.0000 //流通受限股份
8680253870 //某个股东持股数
0.4081 //某某股东所占总股数的比例
 
  • 1
  • 2
  • 3

需求是这样的:股份单位是 万股。比例是百分之多少(%); 
所以对于股份我们需要除以10000,保留2位小数 
对于比例 是要乘以100,保留2位小数。


除法

首先我们来写除法。

/**
 * scale 小数点保留几位
 */
public static BigDecimal divi(double v1,double v2, int scale){
    BigDecimal b1 = new BigDecimal(String.valueOf(v1));
    BigDecimal b2 = new BigDecimal(String.valueOf(v2));
    return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

首先我们是传入两个double类型的参数和精度(小数点保留的位数), 
我们再先转为String类型后,在利用BigDecimal的构造方法来生成BigDecimal对象v1、v2。 
v1.divide(v2…)就是v1除以v2,保留scale为小数,BigDecimal.ROUND_HALF_UP就是我们学的四舍五入。


乘法

public static BigDecimal muli(double v1, double v2, int scale){
    BigDecimal b1 = new BigDecimal(String.valueOf(v1));
    BigDecimal b2 = new BigDecimal(String.valueOf(v2));
    BigDecimal multiply = b1.multiply(b2);
    return multiply.setScale(scale, BigDecimal.ROUND_HALF_UP)
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这个和除法类似,首先把v1 、v2转成BigDecimal对象,然后调用BigDecimal中的multiply方法, 
这个方法不像divide可以设置精度,所以得使用setScale()方法来设置精度。


设置精度(保留几位小数)

public static BigDecimal scale(double v1, int scale){
    //String.valueOf(v1)
    BigDecimal b1 = new BigDecimal(Double.toString(v1));
    return b1.setScale(scale, BigDecimal.ROUND_HALF_UP);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5

这里我要讲的是Double.toString(v1)方法是把v1转成字符串。String.valueOf(v1),也是一样的。 
那两者的区别是什么呢?其实String.valueOf(v1)源码里就是调用Double.toString(v1)的方法。 
上面这种设置精度方法,有个问题: 
要是double v1 = 0.0002,执行scale(v1, 2)时,得到的答案:0.00,其实有时候我们是想保存有效数字。


设置有效精度(保留有效位数)

    /**
     * 保留有效位(eg:0.00002 -- 得到的是0.000020)
     * 
     * @author yutao
     * @return 
     * @date 2016年11月14日下午1:27:28
     */
    public static BigDecimal validScale(double v1, int scale){
        if (scale < 0) {  
             throw new IllegalArgumentException("The scale must be a positive integer or zero");  
         }
        BigDecimal b = new BigDecimal(String.valueOf(v1));  
        BigDecimal divisor = BigDecimal.ONE;  
        MathContext mc = new MathContext(scale);
        return b.divide(divisor, mc);
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这里就用到了MathContext类,它的构造方法有:

1、MathContext(int setPrecision)
2、MathContext(int setPrecision, RoundingMode setRoundingMode)
3、MathContext(String val)
 
  • 1
  • 2
  • 3

参数setPrecision是指有效位数,不是指保留小数多少位。之前就在这里坑到过。 
参数setRoundingMode是指舍入模式。这个和BigDecimal类似。 
也就是说要想设置有效位,就是通过MathContext来设置的。 
一般常用第1、第2中构造方法

枚举常量摘要 
ROUND_CEILING 
向正无限大方向舍入的舍入模式。 
ROUND_DOWN 
向零方向舍入的舍入模式。 
ROUND_FLOOR 
向负无限大方向舍入的舍入模式。 
ROUND_HALF_DOWN 
向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向下舍入。 
ROUND_HALF_EVEN 
向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。 
ROUND_HALF_UP 
向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向上舍入。 
ROUND_UNNECESSARY 
用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入。(默认模式) 
ROUND_UP 
远离零方向舍入的舍入模式。


BigDecimal构造方法应使用String类型的

例子


假设我们先使用Double类型的构造方法。

BigDecimal d1 = new BigDecimal(9.86);  
BigDecimal d2 = new BigDecimal(0.4);  
BigDecimal d3 = d1.divide(d2);  
System.out.println(d3); 
 
  • 1
  • 2
  • 3
  • 4


我们这样执行后,会报如下异常:

Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
    at java.math.BigDecimal.divide(BigDecimal.java:1616)
    at common.ToolsUtil.main(ToolsUtil.java:164)
 
  • 1
  • 2
  • 3


原因:创建BigDecimal时,0.6和0.4是浮动类型的,浮点型放入BigDecimal内,其存储值为

9.8599999999999994315658113919198513031005859375
0.40000000000000002220446049250313080847263336181640625
 
  • 1
  • 2


这两个浮点数相除时,由于除不尽,而又没有设置精度和保留小数点位数,导致抛出异常。 
但是要是我们使用String构造方法就OK

BigDecimal d1 = new BigDecimal("9.86");  
BigDecimal d2 = new BigDecimal("0.4");  
BigDecimal d3 = d1.divide(d2);  
System.out.println(d3); 
 
  • 1
  • 2
  • 3
  • 4


为什么可以这样呢?接下来,我们探索BigDecimal原理: 
BigDecimal,不可变的、任意精度的有符号十进制数。 
BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度(scale) 组成。 
如果为零或正数,则标度是小数点后的位数。 
如果为负数,则将该数的非标度值乘以 10 的负 scale 次幂。 
因此,BigDecimal 表示的数值是 (unscaledValue × 10-scale)。我们知道BigDecimal有三个主要的构造函数

1
public BigDecimal(double val)

将double表示形式转换为BigDecimal

2

public BigDecimal(int val)

将int表示形式转换为BigDecimal

3

public BigDecimal(String val)

将字符串表示形式转换为BigDecimal
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16


通过这三个构造函数,可以把double类型,int类型,String类型构造为BigDecimal对象, 
在BigDecimal对象内通过BigIntegerintVal存储传递对象数字部分,通过int scale;记录小数点位数, 
通过int precision;记录有效位数(默认为0)。 

BigDecimal的加减乘除就成了BigInteger与BigInteger之间的加减乘除,浮点数的计算也转化为整形的计算, 
可以大大提供性能,并且通过BigInteger可以保存大数字,从而实现真正大十进制的计算, 
在整个计算过程中,还涉及scale的判断和precision判断从而确定最终输出结果。 

通过上面的例子可以看出String的构造函数就是通过BigInteger记录BigDecimal的值, 
使其计算变成BigInteger之间的计算。所以我们一般最好使用String类型的构造方法。


那如果非要使用Double类型的构造方法呢? 
我们可以利用divide设置精度的方式来做

BigDecimal d1 = new BigDecimal(9.86);  
BigDecimal d2 = new BigDecimal(0.4);  
BigDecimal d3 = d1.divide(d2 ,1 , BigDecimal.ROUND_HALF_UP);  
System.out.println(d3); 
 
  • 1
  • 2
  • 3
  • 4


通过/1,然后设置保留小数点方式,以及设置数字保留模式,从而得到两个数乘积的小数部分。 
也就是给它设置好精度和舍入模式,就OK啦。(它就是通过舍入方式得到正确的答案)

《BigDecimal 的那些坑事儿》> http://blog.youkuaiyun.com/ugg/article/details/8213666

Java BigDecimal常用的方法介绍 `BigDecimal` 类在 Java 中提供了高精度的十进制浮点运算能力,这对于需要精确计算的应用程序(如金融应用、科学计算等)至关重要。通过使用 `BigDecimal`,开发者可以避免由于浮点数精度问题导致的计算错误。 阅读详情

相关推荐

【Azure 架构师学习笔记】- Azure Private Endpoint

公有云的其中一个特点是默认允许公网访问, 这就对企业环境带来风险,也是很多年前企业对公有云抵触的其中一个原因,现在这类问题已经很少,因为有了很多技术来确保云上的资源被安全地访问。其中Private endpoint(PE)就起到了很重要的作用。云上的某个资源如VM会创建在特定的网络(VNet/Subnet)上, 而其他如Storage Account , Azure SQL等PaaS服务则没有。如果你需要用VM 来访问这些PaaS资源,VM 就会通过资源的公网IP 来访问。

MVP黄钊吉(發糞塗牆) 1791

Java中的BigDecimal运算,如何解决精度丢失问题?

点击关注公众号,利用碎片时间学习1.我们先看一个例子可以看到在Java中进行浮点数运算的时候,会出现丢失精度的问题。那么我们如果在进行商品价格计算的时候,就会出现问题。很有可能造成我们手中...

Java笔记虾 5505

西门子S7-1200控制伺服/步进电机方法与接线(全)

西门子S7-1200控制伺服/步进电机方法与接线(全) 伺服/步进电机在非标自动化控制中十分常用,但作者发现在各类开源网站上很少有人做西门子1200PLC控制伺服/步进电机的教程,于是今天想着跟大家分享一下。 本文共分为一下几个四个内容。 文章目录西门子S7-1200控制伺服/步进电机方法与接线(全)前言一、准备二、接线1.PLC接线:2.伺服接线:3. 步进电机的接线:三、代码组态代码四、可能出现的问题 前言 工程控制常用到三项异步电机、直流电机等,但他们都无法准确的控制电机角度;于是有了步进电机、

weixin_46060068的博客 7万+

Java BigDecimal 类的创建对象方法以及常用的加减乘除、设置精度方法

因为计算机无法精确表示浮点数,所以在进行数字运算时,如果有 double 或 float 类型的浮点数参与计算,容易出现计算不准确的情况。在一些对精度要求很高的系统中,需要使用 BigDecimal 类来进行精确运行。

长安明月的博客 4295

用Matlab系统辨识工具箱搞定温控系统建模:从阶跃响应到传递函数(附完整代码)

本文详细介绍了如何使用Matlab系统辨识工具箱进行温控系统建模,从阶跃响应数据采集到传递函数参数辨识的全过程。通过实际案例和完整代码,帮助工程师快速掌握PID控制参数整定技巧,提升温控系统设计效率。特别适合单片机开发者和自动化控制工程师参考。

weixin_30736301的博客 310

Fiori Elements 的 Adapting the UI: Analytical List Page 全解析与实战

摘要:本文详细介绍了如何通过SAP Fiori elements的UI Adaptation功能对Analytical List Page(ALP)进行细粒度适配。重点讲解了页头过滤区、图表区域、表格区域等关键组件的可配置属性,包括过滤区折叠/固定、图表类型限制、表格滚动阈值等实用功能。文章强调这些适配均可通过可视化工具完成,无需修改业务代码,并提供了典型场景的实践清单。适用于SAP Fiori elements for OData V2项目,建议配合SAP Fiori tools的Adaptation E

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 438

Java运算如何确保精度(BigDecimal)

Java运算如何确保精度(BigDecimal) 在Java中使用浮点型数字进行运算,可能会出现误差等问题,为提高算数精度,避免计算误差,我们采用BigDecimal类加以描述。 BigDecimal类的含义为:不可变的,任意精度的带符号十进制数。 BigDecimal的高精度的原理:浮点型数值运算时发生精度丢失的原因是,在二进制存储中,正数部分其存储单元并非相连的,而是随着数值的增大,存储位置也在变大,这样在整数运算中由于二进制占有的部分的增多,就会导致其出现运算误差,但如果将其小数部分转换成整数,通过整

qq_45722267的博客 4806

番外篇 | 复现HIC-YOLOv5,助力小目标物体的检测

在本文中提出了一种改进的YOLOv5算法:HIC-YOLOv5,用于小目标检测,具有更好的性能和较低的计算成本。🌈

小哥谈 1131

java BigDecimal计算精度问题

java关于BigDecimal计算精度问题 BigDecimal temp = BigDecimal.ZERO;一、保留2位小数:temp.setScale(2, BigDecimal.ROUND_HALF_UP);二、当需要做除法时 temp.divide(BigDecimal.valueOf(除数), 保留位数, BigDecimal.ROUND_HALF_UP)注意,保留位数为必填参数...

weixin_30480651的博客 332

【数据结构】期末考试复习(考点+例题)

1.线性表定义及其存储实现:顺序表和链接表,优缺点分析(顺序存储可随 机访问,链式存储可灵活利用空间),相关操作的时间复杂度。 2.链表的插入删除操作 3.两种特殊的线性表及其应用:栈、队列 4.栈、队列上的(插入/删除)操作及其特点 5.循环队列满和空的条件 6.能够定义线性表、栈、队列的数据存储结构,能够实现相关操作的算法并 分析时间复杂度(例如插入、删除及其它应用等) 7.理解线性结构的应用( 如根据要求能灵活定义线性表结构,能根据线性表 的插入、删除操作解决实际问题) 二叉树基本概念与性质(高

Molly 的博客 9373

[PCIe5.0] 5.3.1.4.2 D3Cold State

主电源(Vcc)完全断开,仅保留极少量辅助电源(Vaux)用于维持基本唤醒功能设备功能完全关闭,所有寄存器状态丢失(除非有特殊存储)需要完全重新初始化才能恢复工作比D3Hot状态功耗更低,但唤醒延迟更长。

系统固件工程师 455

Java BigDecimal详解

1.引言   float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。但是,商业计算往往要求结果精确,这时候BigDecimal就派上大用场啦。      在java中提供了大数字的操作类,即java.math.BinInte...

LMAKE_nbsp的博客 765

工业级实战:C#上位机集成YOLO实现多目标跟踪(含数据库存储+报表导出)

推理核心:ONNX Runtime实现C#直接推理YOLO模型,GPU优先+CPU降级适配工业机;跟踪核心:SORT算法轻量级实现多目标跟踪,保证工业场景实时性与稳定性;数据核心:MySQL存储+本地缓存,Excel报表标准化导出,满足工业数据追溯与质检需求;部署核心:模型优化、GPU加速、开机自启,保证工业级7x24小时运行。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 479

Java浮点数精度问题与BigDecimal详解

BigDecimal的主要优势在于它能提供高精度的数值计算,这对于金融、科学等领域至关重要。正确的使用方法:了解如何创建和使用BigDecimal是基础,特别是在创建对象时选择正确的方法(比如使用字符串构造器)。精度和性能的平衡:虽然BigDecimal提供了高精度,但也可能影响性能。因此,在实际应用中需要在精度和性能之间找到平衡。实际应用场景:在金融计算、商业账单处理等多种场景中,BigDecimal都能提供精确可靠的计算结果。

宋小黑的博客 1448

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

面试:BigDecimal一定不会丢失精度吗?

点击上方蓝色“程序猿DD”,选择“设为星标”回复“资源”获取独家整理的学习资料!来源 |urlify.cn/ZVN7Nb我们基本已经形成了常识,需要用到金钱的地方要用BigDecima...

程序猿DD 671

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

BigDecimal使用

  java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、...

xc_oo0的博客 63

BigDecimal详解

最近项目中有个需求,需要将库中某个字段的值累加,并精确到小数点后两位,返回前端显示,开始使用的是Double去实现,没想到出了问题,Double数据类型在进行累加操作的时候会丢失精度,所以数据显示的时候,输出的是一个小数点后很长的数据,因此果断改为BigDeciml操作,刚好记录一下BigDemical的一些常用操作。BigDecimal在业务需求比较精准的情况下,是非常必要的,可以避免基本数据类型产生的bug。

Java学习博客 3192

bigdecimal不保留小数_JavaAPI—BigDecimal(BigInteger)大数值应用

关注·点击蓝字,关注我吧“我们知道Java中关于数值类型基本数据类型有int、float、double,双精度浮点型double可以处理16位有效数,int类型的数据可表示的数值范围为-231-231-1这个范围内的所有整数,但是整数的范围却远比前面提到的范围要大的多,这个时候就需要使用BigInteger来表示超出int类型范围的整数;双精度浮点型double可以处理16位有效小数,...

weixin_39629129的博客 2977

关于java中BigDecimal类

Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算...

weixin_34357887的博客 102

java BigDecimal使用(小数点,字符串等转换)

java BigDecimal使用(小数点,字符串等转换)

global_coding的博客 5482

java中数字基本运算、金额运算精度问题小结

一、前言   在我们日常工作中,经常会有涉及到数字的运算,其中金额的运算尤其重要且敏感,因为金额的运算若不注意处理的话,很容易因为精度的丢失,从而导致最终数据的异常,造成严重的系统错误。本文将对java中金额的运算处理进行简单小结。    二、NumberFormat类、Decim...

431

关于Bigdecimal的问题

  Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/...

weixin_33980459的博客 113

Java 大数字运算(BigInteger类和BigDecimal类)

文章目录BigInteger类BigDecimal 在 Java 中提供了用于大数字运算的类,即 java.math.BigInteger 类和 java.math.BigDecimal 类。这两个类用于高精度计算,其中 BigInteger 类是针对整型大数字的处理类,而 BigDecimal 类是针对大小数的处理类。 BigInteger类 BigInteger 类型的数字范围较 Integer 类型的数字范围要大得多。BigInteger 支持任意精度的整数,也就是说在运算中 BigInteger

shuyv的博客 920
上一篇: Java中重载与重写的区别
下一篇: java提高篇-----详解java的四舍五入与保留位
ole_triangle_java
博客等级 码龄10年 82粉丝 83原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值