今日推荐文章:使用云开发平台 0 代码开发一个 AI 智能助手小程序-腾讯云开发者社区-腾讯云
点评:该文章指导用户如何利用腾讯云开发平台创建一个AI智能助手小程序,文章从基础准备工作开始,详细介绍了小程序账号的注册、云开发环境的搭建、AI Agent的创建与配置,直至小程序应用的设计、测试与发布,提供了一条清晰的开发路径;是非常值得学习操作的一篇文章
BigDecimal
大家都不陌生;BigDecimal
是Java
编程语言中的一个类,它位于 java.math
包中。这个类提供了任意精度的定点数,非常适合处理货币计算或其他需要精确小数运算的场景。
但是,这个BigDecimal
使用起来有坑,请注意。
标题是我夸张写的,但千万不要踩踏这些坑。
BigDecimal
的坑首先看一下下面这段代码,它会通过这段单测吗
package com.banmoon;
import org.junit.Assert;
import org.junit.Test;
import java.math.BigDecimal;
public class BigDecimalTest {
@Test
public void initializeTest() {
BigDecimal v1 = new BigDecimal("0.01");
BigDecimal v2 = new BigDecimal(0.01);
Assert.assertEquals(0, v1.compareTo(v2));
}
}
如果你的回答是通过,那么你就踩坑了
为什么会这样呢,我决定打印一下他们的值,结果如下
可是为什么会这样呢?
原来,当我们使用这个Double
类型传入时,BigDecimal
为了不丢失Double
的精度,将精度完完整整的保留了下来。
如果有需要将Double
类型转为BigDecimal
类型的时候,建议使用BigDecimal.valueOf(0.01);
重新执行一下单测,查看效果
package com.banmoon;
import org.junit.Assert;
import org.junit.Test;
import java.math.BigDecimal;
public class BigDecimalTest {
@Test
public void initializeTest() {
BigDecimal v1 = new BigDecimal("0.01");
BigDecimal v2 = BigDecimal.valueOf(0.01);
System.out.println("v1:" + v1);
System.out.println("v2:" + v2);
Assert.assertEquals(0, v1.compareTo(v2));
}
}
同样,先看看下面这段代码,并给出是否通过
package com.banmoon;
import org.junit.Assert;
import org.junit.Test;
import java.math.BigDecimal;
public class BigDecimalTest {
@Test
public void equalsTest() {
BigDecimal v1 = new BigDecimal("0.01");
BigDecimal v2 = new BigDecimal("0.010");
System.out.println("v1:" + v1);
System.out.println("v2:" + v2);
Assert.assertEquals(v1, v2);
}
}
很多人一眼看上去,就认定为这两个数字是相等的。但结果却是
数值是相等的没错,但找到不同了吗?没错,它俩的精度不同,我们来看下源码
可以看到,在equals()
方法中,除了对比数值大小,还对比了精度;
所以如果仅仅需要对比数值大小的话,还是使用compareTo()
方法吧
这个方法来自于
Comparable<T>
接口,后续我们可以讲讲这个接口的使用 简单使用如下,使用int result = v1.compareTo(v2)
当v1 > v2
时,result = 1
; 当v1 == v2
时,result = 0
; 当v1 < v2
时,result = -1
;
divide
的坑同样这段代码,非常容易理解,会不会以为精度的问题,导致测试不通过?
package com.banmoon;
import org.junit.Assert;
import org.junit.Test;
import java.math.BigDecimal;
public class BigDecimalTest {
@Test
public void divideTest() {
BigDecimal v1 = new BigDecimal("1");
BigDecimal v2 = new BigDecimal("3");
BigDecimal result = v1.divide(v2);
System.out.println(result);
Assert.assertEquals(0, result.compareTo(new BigDecimal("0.33")));
}
}
但实际上,在执行divide()
的时候,它就报错了,根本走不到断言那一步
这实际上也是跟精度有关,大家都知道1/3
是一个无线循环小数,如果我们没有指定结果精度,它就会一直无限循环下去,然后会导致异常
这在源码的说明上也有体现
建议指定精度,并且加上舍入模式,java.math.RoundingMode
package com.banmoon;
import org.junit.Assert;
import org.junit.Test;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalTest {
@Test
public void divideTest() {
BigDecimal v1 = new BigDecimal("1");
BigDecimal v2 = new BigDecimal("3");
// 1 / 3 * 3
BigDecimal m1 = v1.divide(v2, 2, RoundingMode.HALF_UP).multiply(v2);
// 1 * 3 / 3
BigDecimal m2 = v1.multiply(v2).divide(v2, 2, RoundingMode.HALF_UP);
System.out.println("m1:" + m1);
System.out.println("m2:" + m2);
Assert.assertEquals(0, m1.compareTo(m2));
}
}
这段代码大家肯定都猜到了,由于精度的问题,先除的要避免报错,会不可避免的丢失掉精度
所以为了避免这种事情的发生,要额外注意乘除的先后顺序
出了BUG
不会被开除,那是我为了吸引你进来特地写的
但请不要再犯这么些错误了
另外再提供一下BigDecimal
的加减乘除等基本方法
BigDecimal
是 Java 中用于高精度浮点数运算的一个类。它特别适用于金融计算等需要精确控制小数位数和舍入规则的场景。
add(BigDecimal)
方法。BigDecimal a = new BigDecimal("1.23"); BigDecimal b = new BigDecimal("4.56"); BigDecimal sum = a.add(b); // sum = 5.79
subtract(BigDecimal)
方法。BigDecimal difference = a.subtract(b); // difference = -3.33
multiply(BigDecimal)
方法。BigDecimal product = a.multiply(b); // product = 5.6088
divide(BigDecimal, int, RoundingMode)
方法,需要指定结果的小数位数和舍入模式。BigDecimal quotient = a.divide(b, 2, RoundingMode.HALF_UP); // quotient = 0.27
ROUND_UP
:远离零方向舍入。ROUND_DOWN
:向零方向舍入。ROUND_CEILING
:向正无穷方向舍入。ROUND_FLOOR
:向负无穷方向舍入。ROUND_HALF_UP
:四舍五入,相当于通常的四舍五入。ROUND_HALF_DOWN
:五舍六入。ROUND_HALF_EVEN
:银行家舍入法,四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。在使用 BigDecimal
进行运算时,由于其不可变性,每次运算都会返回一个新的 BigDecimal
对象,原对象不会被改变。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。