1. 当前所在位置:
2. 首页
3. 全民捕鱼

## 为什么要使用BigDecimal类型进行高精度运算

在公司，每次进行记账，或者算钱的时候，都会调用一个统一处理的类，名字叫ArithUntil，查了一下Arith就是数学、运算的意思，那么，为什么不直接用+ - * / 来进行对于数字类型数据的运算呢？（Ps.存储数据的时候有的用的int类型比如打折，但是存储金额的时候用的Decimal类型）。

探究其原因，从网上查了查，查到了一个有意思的东西，就是直接输出java运算某些数字的结果，却发现会在千万甚至亿万的小数级上出现错误，并且都是在浮点运算的时候才会出现的一些小误差的结果，这里有两组，大家可以跑一下试试：

public static void main(String[] args) {
System.out.println(1.0-0.8);
System.out.println(1.1+0.8);
System.out.println(1.1*0.9);
System.out.println(606.3/3000);
}
System.out.println(0.05 + 0.01);
System.out.println(1.0 - 0.42);
System.out.println(4.015 * 100);
System.out.println(123.3 / 100);

这个是什么原因呢？原因在于我们的计算机是二进制的。浮点数没有办法是用二进制进行精确表示。我们的CPU表示浮点数由两个部分组成：指数和尾数，这样的表示方法一般都会失去一定的精确度，有些浮点数运算也会产生一定的误差。

https://jingyan.baidu.com/article/425e69e6e93ca9be15fc1626.html

其实java的float只能用来进行科学计算或工程计算，在大多数的商业计算中，一般采用java.math.BigDecimal类来进行精确计算。

下面奉上通用的操作类（网上的好像都是这样写的）：

import java.math.BigDecimal;
/**
* 由于Java的简单类型不能够精确的对浮点数进行运算，这个工具类提供精
* 确的浮点数运算，包括加减乘除和四舍五入。
*/
public class ArithUntil{ //默认除法运算精度
private static final int DEF_DIV_SCALE = 10; //这个类不能实例化
private ArithUntil(){
}
/**
* 提供精确的加法运算。
* @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));
}
/**
* 提供精确的减法运算。
* @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 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();
}
/**
* 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到
* 小数点以后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();
}
/**
* 提供精确的小数位四舍五入处理。
* @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));
BigDecimal one = new BigDecimal("1");
return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
};

1）用float或者double变量构建BigDecimal对象。

2）通过调用BigDecimal的加，减，乘，除等相应的方法进行算术运算。

3）把BigDecimal对象转换成float，double，int等类型。

一般来说，可以使用BigDecimal的构造方法或者静态方法的valueOf()方法把基本类型的变量构建成BigDecimal对象。

BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组成。如果为零或正数，则标度是小数点后的位数。如果为负数，则将该数的非标度值乘以 10 的负scale 次幂。因此，BigDecimal表示的数值是(unscaledValue × 10-scale)。

1、valueOf方法

public   static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannotfastpath
// 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));
}

public BigDecimal   add(BigDecimal augend) {
long xs =this.intCompact; //整型数字表示的BigDecimal,例a的intCompact值为122
long ys = augend.intCompact;//同上
BigInteger fst = (this.intCompact !=INFLATED) ?null :this.intVal;//初始化BigInteger的值，intVal为BigDecimal的一个BigInteger类型的属性
BigInteger snd =(augend.intCompact !=INFLATED) ?null : augend.intVal;
int rscale =this.scale;//小数位数

long sdiff = (long)rscale - augend.scale;//小数位数之差
if (sdiff != 0) {//取小数位数多的为结果的小数位数
if (sdiff < 0) {
int raise =checkScale(-sdiff);
rscale =augend.scale;
if (xs ==INFLATED ||
(xs = longMultiplyPowerTen(xs,raise)) ==INFLATED)
fst =bigMultiplyPowerTen(raise);
}else {
int raise =augend.checkScale(sdiff);
if (ys ==INFLATED ||(ys =longMultiplyPowerTen(ys,raise)) ==INFLATED)
snd = augend.bigMultiplyPowerTen(raise);
}
}
if (xs !=INFLATED && ys !=INFLATED) {
long sum = xs + ys;
if ( (((sum ^ xs) &(sum ^ ys))) >= 0L)//判断有无溢出
return BigDecimal.valueOf(sum,rscale);//返回使用BigDecimal的静态工厂方法得到的BigDecimal实例
}
if (fst ==null)
fst =BigInteger.valueOf(xs);//BigInteger的静态工厂方法
if (snd ==null)
snd =BigInteger.valueOf(ys);
{"ft_lang_label":"__label__zh","ft_lang_prob":0.5002578,"math_prob":0.9863357,"size":5299,"snap":"2019-13-2019-22","text_gpt3_token_len":2440,"char_repetition_ratio":0.17639282,"word_repetition_ratio":0.13496932,"special_character_ratio":0.25948292,"punctuation_ratio":0.20918985,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.98727196,"pos_list":[0,1,2,3,4],"im_url_duplicate_count":[null,3,null,3,null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2019-03-24T23:16:42Z\",\"WARC-Record-ID\":\"<urn:uuid:10c3765b-4f5f-4adb-a0c9-8c9b9390b53f>\",\"Content-Length\":\"29290\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:35bcc7aa-aeb2-47a5-b76a-9ce0d410dc5d>\",\"WARC-Concurrent-To\":\"<urn:uuid:47ffb08f-8809-4187-a79e-08a2e649ada2>\",\"WARC-IP-Address\":\"59.63.247.227\",\"WARC-Target-URI\":\"https://www.szbelle.com/qmby/826.html\",\"WARC-Payload-Digest\":\"sha1:VPWSSFNLTVJJL2VG2AW3DH47KSRKBDSL\",\"WARC-Block-Digest\":\"sha1:X7EMDJ26ALN4RCVOHTMD236QCILN25BS\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2019/CC-MAIN-2019-13/CC-MAIN-2019-13_segments_1552912203529.38_warc_CC-MAIN-20190324230359-20190325012359-00145.warc.gz\"}"}