目录
本文采用的是jdk11版本的java。
BigDecimal是用来记录计算精确小数的工具,因为double计数会导致数据出错,所以在对小数精度有高度需求的行业中比如买卖商品的价格,用户的余额等等,会使用BigDecimal记录数据。
构造方法
上图是我在api中的截图,通过上图我们可以看到一个数据作为参数时,BigDecimal对象将其值存储。
无MathContext的构造方法
下面给出一个参数的构造方法得到BigDecimal对象的代码示例:
public static void main(String[] args) {
char[] charTest = {'1','2'};
double doubleTest = 1.5;
int intTest = 2;
long longTest = 200L;
String stringTest = "1231546";
BigDecimal bdtest1 = new BigDecimal(charTest);
BigDecimal bdtest2 = new BigDecimal(doubleTest);
BigDecimal bdtest3 = new BigDecimal(intTest);
BigDecimal bdtest4 = new BigDecimal(longTest);
BigDecimal bdtest5 = new BigDecimal(stringTest);
System.out.println("charTest(bdtest1) = " + bdtest1);
System.out.println("doubleTest(bdtest2) = " + bdtest2);
System.out.println("intTest(bdtest3) = " + bdtest3);
System.out.println("longTest(bdtest4) = " + bdtest4);
System.out.println("stringTest(bdtest5) = " + bdtest5);
}
下面是运行结果图:
在这之外,我还发现实际上他对String类型以及char[]类型的数据内容是有一定的要求的,我发现有很多情况他会报错,比如:
1.有两个小数点
2.有不为数字 也不是e的字母
3.数字过大,导致越界
我对上述情况都做了试验,其中的代码如下:
1.两个小数点的情况:
String srtTest = "123.123.123";
BigDecimal test = new BigDecimal(srtTest);//26行出错
System.out.println(srtTest);
System.out.println(test);
运行结果图:
2.出现不为数字,也不是e的字符
String srtTest = "123q123";
BigDecimal test = new BigDecimal(srtTest);//26行出错
System.out.println(srtTest);
System.out.println(test);
运行结果图:
(因为异常信息过长省略了一点点)
3.数字过大越界
String srtTest = "4294967296e4294967296";//4294967296 = 2的32次方
BigDecimal test = new BigDecimal(srtTest);//26行出错
System.out.println(srtTest);
System.out.println(test);
运行结果图:
除了单个参数的构造方法,还有与char[]类型有关的三个参数的构造方法,其中另外两个参数的offset是第一个要考虑的下标,len是长度,下面是示例:
char[] charTest = {'1','2','3','4','5'};
BigDecimal bdtest1 = new BigDecimal(charTest,2,2);
System.out.println("bdtest1 = " + bdtest1);
之后是结果图示:
有MathContext的构造方法
我们不难从api的图中发现,很多构造方法都有MathContext这个类的对象,那么他到底是什么呢?我们先研究一下这个MathContext。
什么是MathContext
我们可以通过api的描述得知,这是一个不可变对象,我们再来看看它的构造方法:
通过描述我们可以得知,这个int类型的setPrecision是精度,用来描述多少位数,并且这个数是非负的,除此之外,我们还发现了一个叫做RoundingMode的东西,我们也来研究一下。
通过这个api的图我们也能看懂一点点这个东西是啥,但是下面的这一串乱码,应该是机翻翻不动了导致的吧…
看了这个介绍之后我们发现这是一种枚举类型,那么他都有哪些值呢:
这一下通过描述我们大概就明白了,MathContext通过这个RoundingMode来决定舍入的模式,如果不做特殊的要求,默认情况下的是HALF_UP模式,也就是需要舍入时,判断这个数等于最近的满足精度要求的数,如果有两个满足精度要求的数距离需要舍入的数相同,那么选择更大的那个数,我们可以通过下面的例子来了解:
double doubleTest1 = 11.55;
double doubleTest2 = 11.54;
MathContext mcTest = new MathContext(3);
BigDecimal bdtest1 = new BigDecimal(doubleTest1,mcTest);
BigDecimal bdtest2 = new BigDecimal(doubleTest2,mcTest);
System.out.println("bdtest1 = " + bdtest1);
System.out.println("bdtest2 = " + bdtest2);
结果图示:
这是使用默认形式的RoundingMode来执行的结果,下面我尝试采用不同形式的RoundingMode来尝试着构造不同的MathContext,再构造BigDecimal
不同RoundingMode下的MathContext
1.CEILING
//CEILING是向正无穷取值
RoundingMode rmTest = RoundingMode.CEILING;
MathContext mcTest = new MathContext(1,rmTest);
double numTest1 = 2.4;
double numTest2 = 2.5;
double numTest3 = 2.6;
double numTest4 = -2.4;
double numTest5 = -2.5;
double numTest6 = -2.6;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);
BigDecimal bdtest2 = new BigDecimal(numTest2,mcTest);
BigDecimal bdtest3 = new BigDecimal(numTest3,mcTest);
BigDecimal bdtest4 = new BigDecimal(numTest4,mcTest);
BigDecimal bdtest5 = new BigDecimal(numTest5,mcTest);
BigDecimal bdtest6 = new BigDecimal(numTest6,mcTest);
System.out.println("bdtest1 = " + bdtest1);
System.out.println("bdtest2 = " + bdtest2);
System.out.println("bdtest3 = " + bdtest3);
System.out.println("bdtest4 = " + bdtest4);
System.out.println("bdtest5 = " + bdtest5);
System.out.println("bdtest6 = " + bdtest6);
结果图示:
2.DOWN
//DOWN是向0取值
RoundingMode rmTest = RoundingMode.DOWN;
MathContext mcTest = new MathContext(1,rmTest);
double numTest1 = 2.4;
double numTest2 = 2.5;
double numTest3 = 2.6;
double numTest4 = -2.4;
double numTest5 = -2.5;
double numTest6 = -2.6;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);
BigDecimal bdtest2 = new BigDecimal(numTest2,mcTest);
BigDecimal bdtest3 = new BigDecimal(numTest3,mcTest);
BigDecimal bdtest4 = new BigDecimal(numTest4,mcTest);
BigDecimal bdtest5 = new BigDecimal(numTest5,mcTest);
BigDecimal bdtest6 = new BigDecimal(numTest6,mcTest);
System.out.println("bdtest1 = " + bdtest1);
System.out.println("bdtest2 = " + bdtest2);
System.out.println("bdtest3 = " + bdtest3);
System.out.println("bdtest4 = " + bdtest4);
System.out.println("bdtest5 = " + bdtest5);
System.out.println("bdtest6 = " + bdtest6);
结果图示:
3.FLOOR
//FLOOR是向负无穷大舍入
RoundingMode rmTest = RoundingMode.FLOOR;
MathContext mcTest = new MathContext(1,rmTest);
double numTest1 = 2.4;
double numTest2 = 2.5;
double numTest3 = 2.6;
double numTest4 = -2.4;
double numTest5 = -2.5;
double numTest6 = -2.6;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);
BigDecimal bdtest2 = new BigDecimal(numTest2,mcTest);
BigDecimal bdtest3 = new BigDecimal(numTest3,mcTest);
BigDecimal bdtest4 = new BigDecimal(numTest4,mcTest);
BigDecimal bdtest5 = new BigDecimal(numTest5,mcTest);
BigDecimal bdtest6 = new BigDecimal(numTest6,mcTest);
System.out.println("bdtest1 = " + bdtest1);
System.out.println("bdtest2 = " + bdtest2);
System.out.println("bdtest3 = " + bdtest3);
System.out.println("bdtest4 = " + bdtest4);
System.out.println("bdtest5 = " + bdtest5);
System.out.println("bdtest6 = " + bdtest6);
结果图示:
4.HALF_DOWN
//HALF_DOWN是最近舍入,如果相同大小,则向下舍入
RoundingMode rmTest = RoundingMode.HALF_DOWN;
MathContext mcTest = new MathContext(1,rmTest);
double numTest1 = 2.4;
double numTest2 = 2.5;
double numTest3 = 2.6;
double numTest4 = -2.4;
double numTest5 = -2.5;
double numTest6 = -2.6;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);
BigDecimal bdtest2 = new BigDecimal(numTest2,mcTest);
BigDecimal bdtest3 = new BigDecimal(numTest3,mcTest);
BigDecimal bdtest4 = new BigDecimal(numTest4,mcTest);
BigDecimal bdtest5 = new BigDecimal(numTest5,mcTest);
BigDecimal bdtest6 = new BigDecimal(numTest6,mcTest);
System.out.println("bdtest1 = " + bdtest1);
System.out.println("bdtest2 = " + bdtest2);
System.out.println("bdtest3 = " + bdtest3);
System.out.println("bdtest4 = " + bdtest4);
System.out.println("bdtest5 = " + bdtest5);
System.out.println("bdtest6 = " + bdtest6);
结果图示:
5.HALF_EVEN
//HALF_EVEN是最近舍入,如果相同大小,则向偶数舍入
RoundingMode rmTest = RoundingMode.HALF_EVEN;
MathContext mcTest = new MathContext(1,rmTest);
double numTest1 = 2.4;
double numTest2 = 2.5;
double numTest3 = 2.6;
double numTest4 = -2.4;
double numTest5 = -2.5;
double numTest6 = -2.6;
double numTest7 = 3.5;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);
BigDecimal bdtest2 = new BigDecimal(numTest2,mcTest);
BigDecimal bdtest3 = new BigDecimal(numTest3,mcTest);
BigDecimal bdtest4 = new BigDecimal(numTest4,mcTest);
BigDecimal bdtest5 = new BigDecimal(numTest5,mcTest);
BigDecimal bdtest6 = new BigDecimal(numTest6,mcTest);
BigDecimal bdtest7 = new BigDecimal(numTest7,mcTest);
System.out.println("bdtest1 = " + bdtest1);
System.out.println("bdtest2 = " + bdtest2);
System.out.println("bdtest3 = " + bdtest3);
System.out.println("bdtest4 = " + bdtest4);
System.out.println("bdtest5 = " + bdtest5);
System.out.println("bdtest6 = " + bdtest6);
System.out.println("bdtest7 = " + bdtest7);
结果图示:
6.HALF_UP
这一种也是不加RoundingMode时默认的模式
//HALF_UP是最近舍入,如果相同大小,则向向上舍入
RoundingMode rmTest = RoundingMode.HALF_UP;
MathContext mcTest = new MathContext(1,rmTest);
double numTest1 = 2.4;
double numTest2 = 2.5;
double numTest3 = 2.6;
double numTest4 = -2.4;
double numTest5 = -2.5;
double numTest6 = -2.6;
double numTest7 = 3.5;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);
BigDecimal bdtest2 = new BigDecimal(numTest2,mcTest);
BigDecimal bdtest3 = new BigDecimal(numTest3,mcTest);
BigDecimal bdtest4 = new BigDecimal(numTest4,mcTest);
BigDecimal bdtest5 = new BigDecimal(numTest5,mcTest);
BigDecimal bdtest6 = new BigDecimal(numTest6,mcTest);
BigDecimal bdtest7 = new BigDecimal(numTest7,mcTest);
System.out.println("bdtest1 = " + bdtest1);
System.out.println("bdtest2 = " + bdtest2);
System.out.println("bdtest3 = " + bdtest3);
System.out.println("bdtest4 = " + bdtest4);
System.out.println("bdtest5 = " + bdtest5);
System.out.println("bdtest6 = " + bdtest6);
System.out.println("bdtest7 = " + bdtest7);
结果图示:
7.UNNECESSARY
//UNNECESSARY是要求数字不用舍入,如果需要舍入,则报异常
RoundingMode rmTest = RoundingMode.UNNECESSARY;
MathContext mcTest = new MathContext(1,rmTest);
double numTest1 = 2;
double numTest2 = 2.0;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);
BigDecimal bdtest2 = new BigDecimal(numTest2,mcTest);
System.out.println("bdtest1 = " + bdtest1);
System.out.println("bdtest2 = " + bdtest2);
正确时的图示:
这种模式下会出现一种异常,因为要求数字是精确的
如果不精确他就会出现这样一个异常java.lang.ArithmeticException: Rounding necessary
代码例子如下:
//UNNECESSARY是要求数字不用舍入,如果需要舍入,则报异常
RoundingMode rmTest = RoundingMode.UNNECESSARY;
MathContext mcTest = new MathContext(1,rmTest);
//此时numTest1数据不合标准,是不精确的数据
double numTest1 = 2.1;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);//75行
System.out.println("bdtest1 = " + bdtest1);
结果图示:
8.UP
//UP是舍入时 数值变化为绝对值更大的那个满足要求的数
RoundingMode rmTest = RoundingMode.UP;
MathContext mcTest = new MathContext(1,rmTest);
double numTest1 = 2.4;
double numTest2 = 2.5;
double numTest3 = 2.6;
double numTest4 = -2.4;
double numTest5 = -2.5;
double numTest6 = -2.6;
double numTest7 = 3.5;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);
BigDecimal bdtest2 = new BigDecimal(numTest2,mcTest);
BigDecimal bdtest3 = new BigDecimal(numTest3,mcTest);
BigDecimal bdtest4 = new BigDecimal(numTest4,mcTest);
BigDecimal bdtest5 = new BigDecimal(numTest5,mcTest);
BigDecimal bdtest6 = new BigDecimal(numTest6,mcTest);
BigDecimal bdtest7 = new BigDecimal(numTest7,mcTest);
System.out.println("bdtest1 = " + bdtest1);
System.out.println("bdtest2 = " + bdtest2);
System.out.println("bdtest3 = " + bdtest3);
System.out.println("bdtest4 = " + bdtest4);
System.out.println("bdtest5 = " + bdtest5);
System.out.println("bdtest6 = " + bdtest6);
System.out.println("bdtest7 = " + bdtest7);
结果图示:
特殊MathContext的构造方法
最后这一种通过String类型构造MathContext的方法,仅仅通过看api是无法完全理解的,或者说是看不懂的,我找到了这一部分代码的源码,如下所示:
/**
* Constructs a new {@code MathContext} from a string.
*
* The string must be in the same format as that produced by the
* {@link #toString} method.
*
* <p>An {@code IllegalArgumentException} is thrown if the precision
* section of the string is out of range ({@code < 0}) or the string is
* not in the format created by the {@link #toString} method.
*
* @param val The string to be parsed
* @throws IllegalArgumentException if the precision section is out of range
* or of incorrect format
* @throws NullPointerException if the argument is {@code null}
*/
public MathContext(String val) {
boolean bad = false;
int setPrecision;
if (val == null)
throw new NullPointerException("null String");
try { // any error here is a string format problem
if (!val.startsWith("precision=")) throw new RuntimeException();
int fence = val.indexOf(' '); // could be -1
int off = 10; // where value starts
setPrecision = Integer.parseInt(val.substring(10, fence));
if (!val.startsWith("roundingMode=", fence+1))
throw new RuntimeException();
off = fence + 1 + 13;
String str = val.substring(off, val.length());
roundingMode = RoundingMode.valueOf(str);
} catch (RuntimeException re) {
throw new IllegalArgumentException("bad string format");
}
if (setPrecision < MIN_DIGITS)
throw new IllegalArgumentException("Digits < 0");
// the other parameters cannot be invalid if we got here
precision = setPrecision;
}
可以看到它大概的意思是通过我们输入的String类型的数据区构造一个MathContext。
通过详细的阅读源码之后我们发现,他要求我们输入的参数String必须格式为
"precision=num roundingMode=xxx"
num必须为一个描述精度的int类型的数字,xxx必须为前面我们提到的RoundingMode中的8个模式之一才能执行,而且中间必须由空格隔开,否则他都会报错。
首先,我们先看一看正确格式的String类型输入的成功案例:
//借用之前的UP模式才试验一下
MathContext mcTest = new MathContext("precision=1 roundingMode=UP");
double numTest1 = 2.4;
double numTest2 = 2.5;
double numTest3 = 2.6;
double numTest4 = -2.4;
double numTest5 = -2.5;
double numTest6 = -2.6;
double numTest7 = 3.5;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);
BigDecimal bdtest2 = new BigDecimal(numTest2,mcTest);
BigDecimal bdtest3 = new BigDecimal(numTest3,mcTest);
BigDecimal bdtest4 = new BigDecimal(numTest4,mcTest);
BigDecimal bdtest5 = new BigDecimal(numTest5,mcTest);
BigDecimal bdtest6 = new BigDecimal(numTest6,mcTest);
BigDecimal bdtest7 = new BigDecimal(numTest7,mcTest);
System.out.println("bdtest1 = " + bdtest1);
System.out.println("bdtest2 = " + bdtest2);
System.out.println("bdtest3 = " + bdtest3);
System.out.println("bdtest4 = " + bdtest4);
System.out.println("bdtest5 = " + bdtest5);
System.out.println("bdtest6 = " + bdtest6);
System.out.println("bdtest7 = " + bdtest7);
结果图示:
可以对比发现,和之前直接使用UP模式和确定精度为1时一样。
然后我们再试验一种错误的方法,看一下它的异常提示:
//这里我们用”,“隔开而不是用空格隔开来试验一下
MathContext mcTest = new MathContext("precision=1,roundingMode=UP");
double numTest1 = 2.4;
BigDecimal bdtest1 = new BigDecimal(numTest1,mcTest);
System.out.println("bdtest1 = " + bdtest1);
结果图示:
bad String format!!
如果不按照他给出的格式输入的话就会出现这个异常提示。
好了,以上就是现阶段BigDecimal的构造方法介绍了,其实还有一种BigInteger的方法没有介绍,因为对BigInteger的了解还不够,所以在介绍BigInteger时再回过头来讲一讲。