Java开发中商业计算请务必使用BigDecimal来进行计算!
2021-03-28 12:25
标签:sub 结果 git 逗号 form 算术运算 字符串 table 出现 今天群里一个初级开发者问为什么测试人员测出来他写的价格计算模块有计算偏差的问题,他检查了半天也没找出问题。这里小胖哥要提醒你,商业计算请务必使用 BigDecimal BigDecimal表示不可变的任意精度带符号十进制数。它由两部分组成: 例如,BigDecimal 3.14的未校正值为314,缩放为2。我们使用BigDecimal进行高精度算术运算。我们还将它用于需要控制比例和舍入行为的计算。如果你的计算是商业计算请务必使用计算精确的 构造BigDecimal实例 我们可以从 我们还可以从 我们发现在这种情况下,结果与预期的结果不同(即0.1)。这是因为:这个转换结果是 在转换为BigDecimal之前,此方法将double转换为其String表示形式。此外,它可以重用对象实例。因此,我们应该优先使用valueOf方法来构造函数。 常用API BigDecimal操作 BigDecimal上的操作就像其他Number类(Integer,Long,Double等)一样,BigDecimal提供算术和比较操作的操作。它还提供了缩放操作,舍入和格式转换的操作。它不会使算术运算符(+ - /*)或逻辑运算符(> ) 过载。相反,我们使用 提取属性 精度,小数位数和符号: 比较大小 我们使用 上面的方法在比较时忽略了小数位。如果你既要比较精度又要比较小数位数那么请使用 四则运算 BigDecimal 提供了以下四则运算的方法: 四舍五入 既然是数学运算就不得不讲四舍五入。比如我们在金额计算中很容易遇到最终结算金额为人民币 格式化 数字格式化可通过操作类 NumberFormat DecimalFormat “0”——表示一位数值,如没有,显示0。如“0000.0000”,整数位或小数位>4,按实际输出,“#”——表示任意位数的整数。如没有,则不显示。在小数点位使用,只表示一位小数,超出部分四舍五入。如:“#”:无小数,小数部分四舍五入。“.#”:整数部分不变,一位小数,四舍五入。“.##”:整数部分不变,二位小数,四舍五入。 总结 今天对 Java开发中商业计算请务必使用BigDecimal来进行计算! 标签:sub 结果 git 逗号 form 算术运算 字符串 table 出现 原文地址:https://blog.51cto.com/14901317/2529863BigDecimal
,浮点做商业运算是不精确的。因为计算机无法使用二进制小数来精确描述我们程序中的十进制小数。《Effective Java》在第48条也推荐“使用BigDecimal来做精确运算”。今天我们就来总结归纳其相关的知识点。
BigInteger
BigDecimal
。String
,character
数组,int
,long
和BigInteger
创建一个BigDecimal
对象:@Test
public void theValueMatches() {
BigDecimal bdFromString = new BigDecimal("0.12");
BigDecimal bdFromCharArray = new BigDecimal(new char[]{‘3‘, ‘.‘, ‘1‘, ‘4‘, ‘1‘, ‘5‘});
BigDecimal bdlFromInt = new BigDecimal(42);
BigDecimal bdFromLong = new BigDecimal(123412345678901L);
BigInteger bigInteger = BigInteger.probablePrime(100, new Random());
BigDecimal bdFromBigInteger = new BigDecimal(bigInteger);
assertEquals("0.12", bdFromString.toString());
assertEquals("3.1415", bdFromCharArray.toString());
assertEquals("42", bdlFromInt.toString());
assertEquals("123412345678901", bdFromLong.toString());
assertEquals(bigInteger.toString(), bdFromBigInteger.toString());
}
double
创建BigDecimal
:@Test
public void whenBigDecimalCreatedFromDouble_thenValueMayNotMatch() {
BigDecimal bdFromDouble = new BigDecimal(0.1d);
assertNotEquals("0.1", bdFromDouble.toString());
}
double
的二进制浮点值的精确十进制表示,其值得结果不是我们可以预测的.我们应该使用String
构造函数而不是double
构造函数。另外,我们可以使用valueOf
静态方法将double
转换为BigDecimal
或者直接使用其未校正数加小数位数 :@Test
public void whenBigDecimalCreatedUsingValueOf_thenValueMatches() {
BigDecimal bdFromDouble = BigDecimal.valueOf(0.1d);
BigDecimal bigFromLong=BigDecimal.valueOf(1,1);
assertEquals("0.1", bdFromDouble.toString());
assertEquals("0.1", bigFromLong.toString());
}
方法名
对应方法相关用法解释
abs()
绝对值,scale不变
add(BigDecimal augend)
加,scale为augend和原值scale的较大值
subtract(BigDecimal augend)
减,scale为augend和原值scale的较大值
multiply(BigDecimal multiplicand)
乘,scale为augend和原值scale的和
divide(BigDecimal divisor)
除,原值/divisor,如果不能除尽会抛出异常,scale与原值一致
divide(BigDecimal divisor, int roundingMode)
除,指定舍入方式,scale与原值一致
divide(BigDecimal divisor, int scale, int roundingMode)
除,指定舍入方式和scale
remainder(BigDecimal divisor)
取余,scale与原值一致
divideAndRemainder(BigDecimal divisor)
除法运算后返回一个数组存放除尽和余数 如
23/3
返回 {7,2}
divideToIntegralValue(BigDecimal divisor)
除,只保留整数部分,但scale仍与原值一致
max(BigDecimal val)
较大值,返回原值与val中的较大值,与结果的scale一致
min(BigDecimal val)
较小值,与结果的scale一致
movePointLeft(int n)
小数点左移,scale为原值scale+n
movePointRight(int n)
小数点右移,scale为原值scale+n
negate()
取反,scale不变
pow(int n)
幂,原值^n,原值的n次幂
scaleByPowerOfTen(int n)
相当于小数点右移n位,原值*10^n
BigDecimal
相应的方法 - 加,减,乘,除和比较。并且BigDecimal
具有提取各种属性的方法。@Test
public void whenGettingAttributes_thenExpectedResult() {
BigDecimal bd = new BigDecimal("-12345.6789");
assertEquals(9, bd.precision());
assertEquals(4, bd.scale());
assertEquals(-1, bd.signum());
}
compareTo
方法比较两个BigDecimal
的值:@Test
public void whenComparingBigDecimals_thenExpectedResult() {
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
BigDecimal bd3 = new BigDecimal("2.0");
assertTrue(bd1.compareTo(bd3) 0);
assertTrue(bd1.compareTo(bd2) == 0);
assertTrue(bd1.compareTo(bd3) = 0);
assertTrue(bd1.compareTo(bd3) != 0);
}
equals
方法:@Test
public void whenEqualsCalled_thenSizeAndScaleMatched() {
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
assertFalse(bd1.equals(bd2));
}
ArithmeticException
异常@Test
public void whenPerformingArithmetic_thenExpectedResult() {
BigDecimal bd1 = new BigDecimal("4.0");
BigDecimal bd2 = new BigDecimal("2.0");
BigDecimal sum = bd1.add(bd2);
BigDecimal difference = bd1.subtract(bd2);
BigDecimal quotient = bd1.divide(bd2);
BigDecimal product = bd1.multiply(bd2);
assertTrue(sum.compareTo(new BigDecimal("6.0")) == 0);
assertTrue(difference.compareTo(new BigDecimal("2.0")) == 0);
assertTrue(quotient.compareTo(new BigDecimal("2.0")) == 0);
assertTrue(product.compareTo(new BigDecimal("8.0")) == 0);
}
22.355
的情况。因为货币没有比分更低的单位所以我们要使用精度和舍入模式规则对数字进行剪裁。java提供有两个类控制舍入行为RoundingMode
和MathContext
。MathContext
执行的是IEEE 754R标准目前不太明白其使用场景,我们使用的比较多的是枚举RoundingMode
。它提供了八种模式:
java.text.NumberFormat
和java.text.DecimalFormat
提供的api进行操作。其实我们只需要使用java.text.DecimalFormat
,因为它代理了NumberFormat
。我们来看一下它们的api:
DecimalFormat
除了能代理上面的NumberFormat
以外,还提供了基于pattern
字符串的格式化风格,有点类似格式化时间一样。我们来看看pattern
的规则:
“.”——表示小数点。注意一个pattern中只能出现一次,超过一次将格式化异常。
“,”——与模式“0”一起使用,表示逗号。注意一定不能在小数点后用,否则格式化异常。BigDecimal
进行了总结归纳,这篇文章建议你收藏备用,也可以转给其他需要的同学。
文章标题:Java开发中商业计算请务必使用BigDecimal来进行计算!
文章链接:http://soscw.com/index.php/essay/69037.html