Java-Math
java.math 包
BigDecimal
浮点数精度有限,在运算过程中很有可能会损失精度,因此 Java 提供了 BigDecimal 来精确地进行小数的表示和计算。
不要直接用 double 构造 BigDecimal
使用 BigDecimal(double val)
直接构造的 BigDecimal 可能与我们的预期不符,比如 3.5d 转为 BigDecimal 后可能是 3.4999999999999…,后续如果想直接四舍五入就会得到 3
原因并不是 double 转为 BigDecimal 后损失精度,这个描述是错误的,因为本身 3.5d 这个双精度浮点值在内存里存储的可能就是 3.4999999999999999,是一个非常接近 3.5 的浮点值,转为 BigDecimal 后存储了他原本的双精度值,也就是 3.4999999999999999,但这可能不是我们想要的。我们要的是转为 BigDecimal 后还是 3.5
改为使用 BigDecimal(String val)
构造即可,比如 BigDecimal(String.valueOf(3.5d))
或 BigDecimal(Double.toString(3.5d))
,原理是 String.valueOf(double)
会调用 Double.toString(double)
方法,这个方法会对 double 的内部值做近似运算,从而得到符合我们预期的字符串表示的浮点值
有一种静态代码扫描的缺陷就叫做 BigDecimalConstructedFromDoubleCWE,指从 double 构建了 BigDecimal,例如 new BigDecimal(2.3),应改为 new BigDecimal(String.valueOf(2.3))
https://cwe.mitre.org/data/definitions/665.html
https://blog.csdn.net/ystyaoshengting/article/details/52252615
RoundingMode 舍入模式
建议使用 RoundingMode 枚举类指定,也可通过 BigDecimal 内置的静态常量指定:
ROUND_UP
绝对值向上舍入,1.6舍入为2,-2.5舍入为-3ROUND_DOWN
绝对值向下舍入,1.6舍入为1,-2.5舍入为-2ROUND_HALF_UP
绝对值四舍五入,.5向上取整,2.5舍入为3,-2.5舍入为-3ROUND_HALF_DOWN
绝对值四舍五舍,.5向下取整,2.5舍入为2,-2.5舍入为-2
setScale(int, int) 设置精度(小数点)
public BigDecimal setScale(int newScale, int roundingMode)
public BigDecimal setScale(int newScale, RoundingMode roundingMode)
返回一个修改了精度的 BigDecimal
注意:由于 BigDecimal 对象本身是不可修改的,调用此方法后原 BigDecimal 对象的精度不会变,只能通过返回值获取修改了精度的 BigDecimal
例,double 转 BigDecimal 保留两位小数
@Test
public void testSetScale() {
double d = 1.534;
BigDecimal bigDecimal = new BigDecimal(d);
System.out.println(bigDecimal);
BigDecimal scaledBigDecimal = bigDecimal.setScale(2, RoundingMode.HALF_UP);
System.out.println(scaledBigDecimal);
}
输出结果:
1.5340000000000000301980662698042578995227813720703125
1.53
divide() 除法
public BigDecimal divide(BigDecimal divisor)
不指定精度和舍入除不尽时会抛 ArithmeticException 异常
如果不指定精度 scale 和舍入模式 roundingMode 除不尽时会抛 ArithmeticException 异常。
例如,不设置精度BigDecimal 10 除以 3,报错
@Test
public void testDivideWithoutScale() {
BigDecimal bigDecimal = new BigDecimal(10).divide(new BigDecimal(3));
}
抛出运行时异常
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
divide(BigDecimal, scale, roundingMode) 除法指定精度和舍入模式
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
BigDecimal 除法,返回 (this / divisor)
参数:divisor
除数scale
精度,表示小数点后的位数roundingMode
舍入模式,建议使用 RoundingMode 枚举,也可以使用 BigDecimal 内置的静态常量
例如,10 除以 3,保留2位小数点,四舍五入
@Test
public void testDivide() {
BigDecimal bigDecimal = new BigDecimal(10).divide(new BigDecimal(3), 2, BigDecimal.ROUND_HALF_UP);
System.out.println(bigDecimal);
System.out.println(bigDecimal.doubleValue());
}
结果:
3.33
3.33
intValueExact() BigDecimal 转 int
如果 BigDecimal 有小数会抛异常
Math
ceil(double) 向上取整
返回大于等于入参的最小整数,但返回结果是 double 双精度表示的整数,需要 int 类型的话需要强制转换下。
public static double ceil(double a) {
return StrictMath.ceil(a); // default impl. delegates to StrictMath
}
可以使用下面的公式进行整数除法的向上取整 divided / divisor
int ret = (divided + divisor - 1) / divisor;
Math.ceil 精度丢失导致结果不符合预期
Math.ceil(799 / 200)
结果是 3,可能和预期的不一样。
因为整数除法 799 / 200 结果是 3(整数除法向下取整),转为 double 是 3.0,后续向上取整结果也是 3.0
正确用法可以先将入参的整数转为 double,比如 Math.ceil(799d / 200)
public void testCeil() {
System.out.println(Math.ceil(799 / 200)); // 3.0
System.out.println(Math.ceil(799d / 200)); // 4.0
}
floor(double) 向下取整
返回小于等于入参的最大整数,返回结果是 double 类型
public static double floor(double a) {
return StrictMath.floor(a); // default impl. delegates to StrictMath
}
round(double/float) 四舍五入
public static long round(double a)
public static int round(float a)
rint(double) 最接近的整数
返回与入参最接近的整数,返回类型是 double。
如果有两个整数到入参的距离相等,返回偶数值,比如 100.5 的 rint 值是 100,而 100.5 的 round 值是 101
public static double rint(double a) {
return StrictMath.rint(a); // default impl. delegates to StrictMath
}
toIntExact(long) long转int溢出抛异常
如果超出 int 表示范围会抛异常
public static int toIntExact(long value) {
if ((int)value != value) {
throw new ArithmeticException("integer overflow");
}
return (int)value;
}
上一篇 Eclipse-MAT
下一篇 Xenon
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: