BigDecimal的使用和一些坑

news/2024/11/28 11:48:04/

在金融领域进行业务计算时,因为要保证精度的准确,通常不建议使用float和double进行数学计算。
float和double类型主要是为了科学计算和工程计算而设计的。他们执行二进制浮点运算,这是为了在广泛的数字范围上提供较为精确的快速近似计算而精心设计的。然而,它们并没有提供完全精确的结果,所以我们不应该用于精确计算的场合。float和double类型尤其不适合用于货币运算,因为要让一个float或double精确的表示0.1或者10的任何其他负数次方值是不可能的(其实道理很简单,十进制系统中能不能准确表示出1/3呢?同样二进制系统也无法准确表示1/10)

为什么浮点型运算会造成精度丢失

首先我们要搞清楚下面两个问题:

 (1) 十进制整数如何转化为二进制数算法很简单。举个例子,11表示成二进制数:11/2=5 余   15/2=2   余   12/2=1   余   01/2=0   余   10结束         11二进制表示为(从下往上):1011这里提一点:只要遇到除以后的结果为0了就结束了,大家想一想,所有的整数除以2是不是一定能够最终得到0。换句话说,所有的整数转变为二进制数的算法会不会无限循环下去呢?绝对不会,整数永远可以用二进制精确表示 ,但小数就不一定了。(2) 十进制小数如何转化为二进制数算法是乘以2直到没有了小数为止。举个例子,0.9表示成二进制数0.9*2=1.8   取整数部分 10.8(1.8的小数部分)*2=1.6    取整数部分 10.6*2=1.2   取整数部分 10.2*2=0.4   取整数部分 00.4*2=0.8   取整数部分 00.8*2=1.6 取整数部分 10.6*2=1.2   取整数部分 0.........      0.9二进制表示为(从上往下): 1100100100100......注意:上面的计算过程循环了,也就是说*2永远不可能消灭小数部分,这样算法将无限下去。很显然,小数的二进制表示有时是不可能精确的 。其实道理很简单,十进制系统中能不能准确表示出1/3呢?同样二进制系统也无法准确表示1/10。这也就解释了为什么浮点型减法出现了"减不尽"的精度丢失问题。

所以在进行精确计算时,java中推荐使用BigDecimal,同样在BigDecimal的使用中也有一些需要注意的地方,如果注意就可能导致同样的精度损失问题。

1.在bigDecimal初始化时,不要使用double或者float类型的值传入构造器

Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。

对比可知,test2仍然会出现精度问题,而在创建BigDecimal对象时,参数为字符串就不会出现精度问题
所以总结如下


```java
//使用String构造
BigDecimal b1 = new BigDecimal("0.1");
//或者是:
BigDecimal b1 = BigDecimal.valueOf(0.1);
//点开valueOf的源码,可以看到在源码中也是用new BigDecimal(String);返回一个BigDecimal对象的
//源码如下:
public static BigDecimal valueOf(double val) {// Reminder: a zero double returns '0.0', so we cannot fastpath// to use the constant ZERO.  This might be important enough to// justify a factory approach, a cache, or a few private// constants, later.return new BigDecimal(Double.toString(val));
}

``

2.在进行BigDecimal数值比较时不要使用equals进行比较

使用equals进行比较会比较值的大小和精度的大小,即0.00和0.000是不相等的,要使用compareTo()来进行比较。

BigDecimal使用中的报错

Division is undefined错误

  1. 此错误仅在使用BigDecimal做除法时,且0/0的情况下才会提示。
    x/0时,仅提示Division by zero。
  2. BigDecimal判断一个值是否为0时,不能使用equals,因为equals会比较值的大小和精度的大小,即0.00 和 0.000是不同的。
    需要使用 x.compareTo(BigDecimal.ZERO) == 0 来判断。
  3. 且使用divide做除法时,标准的形式为 x.divide(y, scale,rm)
    如果不指明scale的值,会默认使用 x.scale - y.scale的值替代,如果此时值为负数,则会报错 Division undefined。
    且不指定scale 和 rm四舍五入方式,如果遇到 1/3 这种除不尽的情况,会报如下错误:
    Non-terminating decimal expansion; no exact representable decimal result.

BigDecimalUtil工具类 --解决精度问题


```java
public class BigDecimalUtil {//防止工具类在外部实例化private BigDecimalUtil(){}public static BigDecimal add(double v1, double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.add(b2);}public static BigDecimal sub(double v1,double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.subtract(b2);}public static BigDecimal mul(double v1,double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.multiply(b2);}public static BigDecimal div(double v1,double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.divide(b2,2,BigDecimal.ROUND_HALF_UP);//四舍五入,保留2位小数}
}

``

BigDecimal的开平方方法,使用牛顿迭代法

在这里插入图片描述
在浮点数进行计算时可以使用Math.sqrt()方法进行开方运算,在使用BigDecimal时没有提供开方方法,需要我们自己实现。
牛顿迭代法
牛顿迭代法,又称切线法,由牛顿首次提出。其算法详细过程如下图所示:

以方程 x2=nx2=n 为例,令 f(x)=x2−nf(x)=x2−n,也就是相当于求解 f(x)=0f(x)=0 的解。首先随便找一个初始值 x0x0,如果 x0x0 不是解,做一个经过 (x0,f(x0))(x0,f(x0)) 这个点的切线,与轴的交点为 x1x1。同理,如果 x1x1 不是解,做一个经过(x1,f(x1))(x1,f(x1)) 这个点的切线,与轴的交点为 x2x2。 以此类推……以这样的方式得到的会无限趋近于 f(x)=0f(x)=0 的解。

判断是否是的解有两种方法:1. 直接计算的值判断f(x)是否为0;2. 判断前后两个解和是否无限接近。

经过这个点 (xi,f(xi))(xi,f(xi)) 的切线方程为 f(x)=f(xi)+f′(xi)(x−xi)f(x)=f(xi)+f′(xi)(x−xi),
其中,f′(xi)f′(xi) 为 f(xi)f(xi) 的导数,本题中导数为 2x2x。令切线方程等于0(纵轴截距取0),即可求出:
xi+1=xi−f(xi)f′(xi)
xi+1=xi−f(xi)f′(xi)

带入 f(x)=x2−nf(x)=x2−n,继续化简:
xi+1=xi−x2i−n2xi=xi−xi2+n2xi=xi2+n2xi
xi+1=xi−xi2−n2xi=xi−xi2+n2xi=xi2+n2xi

基于上述迭代公式,可以给出了一个求平方根的算法。事实上,这也的确是很多语言中内置的开平方函数的实现方法。牛顿迭代法也同样适用于求解其他多次方程的解。

private BigDecimal sqrt(BigDecimal value, int scale) {if(value.compareTo(BigDecimal.ZERO)==0){return new BigDecimal(0);}BigDecimal num2 = BigDecimal.valueOf(2);int precision = 100;MathContext mc = new MathContext(precision, RoundingMode.HALF_UP);BigDecimal deviation = value;int cnt = 0;while (cnt < precision) {deviation = (deviation.add(value.divide(deviation, 100,BigDecimal.ROUND_HALF_UP))).divide(num2,100,BigDecimal.ROUND_HALF_UP);cnt++;}deviation = deviation.setScale(scale, BigDecimal.ROUND_HALF_UP);return deviation;
}

http://www.ppmy.cn/news/442880.html

相关文章

跑CUDA GPU训练一定要用N卡(NVIDIA)吗,A卡(ATi)不行?

一定得用N卡。 参考文章&#xff1a;为什么A卡不能使用CUDA&#xff0c;一定得N卡么

N卡滤镜打开方法介绍

N卡是许多游戏玩家的首选显卡&#xff0c;性能优越&#xff0c;运行过程中温度也很稳定&#xff0c;带着用户良好的体验感。那么N卡怎么打开滤镜设置呢&#xff1f;很多用户都不知道如何操作&#xff0c;下面小编就来给大家介绍一下方法。 n卡滤镜打开方法介 1、首先你需要拥有…

nvidia是什么

nvidia是一家人工智能计算公司&#xff0c;公司创立于1993年&#xff0c;总部位于美国加利福尼亚州圣克拉拉市&#xff0c;美籍华人Jensen Huang黄仁勋是创始人兼CEO。 推荐&#xff1a;《编程视频》 NVIDIA&#xff08;纳斯达克股票代码&#xff1a;NVDA&#xff09;是一家人工…

计算机显卡n卡,电脑显卡a卡和n卡的区别是什么

很多小伙伴在自己dIy组装电脑的时候选择显卡会感到非常的迷茫&#xff0c;因为不知道应该使用a卡还是选择使用n卡&#xff0c;那么电脑显卡的a卡和n卡之间有什么样的区别呢&#xff1f;下面我们就来给大家普及一下相关的知识。 区别一、生产厂商不同&#xff1a;显卡a卡的生产厂…

CUDA和N卡驱动

Pytorch检测CUDA和cuDNN版本 import torchprint(torch.cuda.is_available()) # 检查cuda是否可用 print(torch.version.cuda) # 查看cuda版本print(torch.backends.cudnn.is_available()) # 检查cudnn是否可用 print(torch.backends.cudnn.version()) # 查看cudnn版本下载…

HTML打游戏时不清晰,怎样设置n卡性能最佳 N卡怎么设置才能玩游戏达到最高性能...

怎么设置N卡的控制面板才能发挥显卡的最大性能&#xff1f; 我是双显卡&#xff0c;N卡是GT550M显存2G&#xff0c;还有一个英特尔HD Graphics Family核心首先在Cortana中搜索并点击“控制面板”选项&#xff0c;如下图所示。 进入控制面板界面之后&#xff0c;选择点击“NVIDI…

N卡和A卡有什么区别?43.248.188.x

A卡和N卡的区别 A卡早期是指采用ATI显卡芯片的显卡&#xff0c;但后面被AMD收购后&#xff0c;A卡其实就是AMD显卡。 A卡作品有镭、X系列。 N卡则指的就是NVIDIA显卡芯片的显卡&#xff0c;N卡作品有GeForce&#xff08;GTX&#xff09;系列&#xff0c;GeForce FX&#x…

千与千寻 学生网页设计与制作

从业多年&#xff0c;设计制作了很多学生网站&#xff0c;这个千寻动漫网站&#xff0c;从构思到完成&#xff0c;大概花了一个星期的时间完成&#xff0c;页面简洁&#xff0c;代码适合学生水平&#xff0c;为本人原创作品。