当前位置 : 首页 » 文章分类 :  开发  »  Java-Math

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舍入为-3
  • ROUND_DOWN 绝对值向下舍入,1.6舍入为1,-2.5舍入为-2
  • ROUND_HALF_UP 绝对值四舍五入,.5向上取整,2.5舍入为3,-2.5舍入为-3
  • ROUND_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

阅读
评论
1.2k
阅读预计5分钟
创建日期 2022-04-26
修改日期 2023-03-24
类别
标签

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论