Docker容器时区错位导致报表数据异常:排查思路与修复方案
2026/9/20 5:29:40
Java 中处理超出基本数据类型范围的超大整数 / 超大小数,Java 提供了专门的java.math包来解决该问题,核心是两个类:BigInteger(超大整数)、BigDecimal(超大小数),下面从核心用法、场景对比、实战案例到避坑技巧全面讲解。
long(最大 9223372036854775807)的取值上限 / 下限。float/double的精度丢失问题(如0.1+0.2≠0.3)。| 类型 | 取值范围 | 精度问题 | 适用场景 |
|---|---|---|---|
| byte/int/long | 固定有限范围 | 无(整数) | 常规小数值整数运算 |
| float/double | 范围大 | 有(浮点丢失) | 非精准的科学计算 |
| BigInteger | 无限制 | 无 | 超大整数精准运算 |
| BigDecimal | 无限制 | 无 | 小数 / 金额精准运算 |
BigInteger不能直接用赋值号创建,必须通过构造方法或静态方法初始化,核心方式如下:
import java.math.BigInteger; public class BigIntegerDemo { public static void main(String[] args) { // 方式1:字符串构造(最常用,支持超大数值)✅ 推荐 BigInteger big1 = new BigInteger("123456789012345678901234567890"); // 方式2:基本类型转换(int/long) BigInteger big2 = BigInteger.valueOf(100L); // 方式3:特殊常量(0、1、10,直接调用,性能更高) BigInteger zero = BigInteger.ZERO; BigInteger one = BigInteger.ONE; BigInteger ten = BigInteger.TEN; } }BigInteger不支持+、-、*、/运算符,必须调用类内封装的方法,所有方法返回新的 BigInteger 对象(原对象不变):
public static void main(String[] args) { BigInteger a = new BigInteger("1000000000000000000"); BigInteger b = new BigInteger("2000000000000000000"); // 1. 加法:a + b BigInteger add = a.add(b); // 2. 减法:a - b BigInteger sub = a.subtract(b); // 3. 乘法:a * b BigInteger mul = a.multiply(b); // 4. 除法:a / b(整除,无余数) BigInteger div = b.divide(a); // 5. 取模:a % b BigInteger mod = a.mod(b); // 6. 幂运算:a^3(a的3次方) BigInteger pow = a.pow(3); // 7. 绝对值 BigInteger abs = new BigInteger("-999").abs(); // 8. 比较大小(返回0相等,1>a大,-1>a小) int compare = a.compareTo(b); System.out.println("加法结果:" + add); System.out.println("幂运算结果:" + pow); }BigInteger 内置了大量实用工具方法,满足大数场景的特殊需求:
public static void main(String[] args) { BigInteger num = new BigInteger("123456"); BigInteger target = new BigInteger("7"); // 1. 进制转换:10进制 → 2进制/16进制 String binary = num.toString(2); // 二进制 String hex = num.toString(16); // 十六进制 // 2. 最大公约数(GCD) BigInteger gcd = num.gcd(target); // 3. 素数检测(isProbablePrime(确定性),参数越大越精准,一般传50即可) boolean isPrime = new BigInteger("9973").isProbablePrime(50); // 4. 取反(相反数) BigInteger negate = num.negate(); System.out.println("10进制转2进制:" + binary); System.out.println("是否为素数:" + isPrime); }double构造 BigDecimal:new BigDecimal(0.1)会得到0.10000000000000000555...,因为double本身存在精度丢失;new BigDecimal("0.1")才能得到精准的0.1;ArithmeticException;import java.math.BigDecimal; public class BigDecimalDemo { public static void main(String[] args) { // ❌ 错误方式:double构造,存在精度丢失 BigDecimal bad = new BigDecimal(0.1); // ✅ 推荐方式1:字符串构造(精准,无丢失) BigDecimal good1 = new BigDecimal("0.1"); // ✅ 推荐方式2:基本类型转换(int/long,无精度问题) BigDecimal good2 = BigDecimal.valueOf(100); // 等价于 new BigDecimal("100") // ✅ 推荐方式3:特殊常量 BigDecimal zero = BigDecimal.ZERO; BigDecimal one = BigDecimal.ONE; BigDecimal ten = BigDecimal.TEN; System.out.println("错误构造:" + bad); // 输出:0.1000000000000000055511151231257827021181583404541015625 System.out.println("正确构造:" + good1); // 输出:0.1 } }同样不支持运算符,需调用封装方法,除法必须指定舍入模式:
public static void main(String[] args) { BigDecimal a = new BigDecimal("0.1"); BigDecimal b = new BigDecimal("0.2"); BigDecimal c = new BigDecimal("3"); // 1. 加法:0.1 + 0.2 = 0.3 ✅ 精准 BigDecimal add = a.add(b); // 2. 减法:0.2 - 0.1 = 0.1 BigDecimal sub = b.subtract(a); // 3. 乘法:0.1 * 0.2 = 0.02 BigDecimal mul = a.multiply(b); // 4. 除法:必须指定【舍入模式】,示例:1 ÷ 3 保留2位小数 BigDecimal div = BigDecimal.ONE.divide(c, 2, BigDecimal.ROUND_HALF_UP); System.out.println("0.1+0.2=" + add); // 输出:0.3 System.out.println("1÷3=" + div); // 输出:0.33 }舍入模式是 BigDecimal 的重中之重,决定了小数的取舍规则,金融场景优先用ROUND_HALF_UP(四舍五入),所有舍入模式均为BigDecimal的静态常量:
| 舍入模式常量 | 含义 | 示例(保留 2 位) |
|---|---|---|
| ROUND_HALF_UP ✅(推荐) | 四舍五入(银行家常用) | 2.346 → 2.35 |
| ROUND_UP | 向上进 1(远离 0) | 2.341 → 2.35 |
| ROUND_DOWN | 向下截断(靠近 0) | 2.349 → 2.34 |
| ROUND_HALF_DOWN | 五舍六入 | 2.345 → 2.34;2.346→2.35 |
| ROUND_CEILING | 向上取整(正无穷方向) | -2.34 → -2.34;2.34→2.35 |
| ROUND_FLOOR | 向下取整(负无穷方向) | -2.34 → -2.35;2.34→2.34 |
// 保留2位小数,四舍五入 BigDecimal num = new BigDecimal("123.4567"); BigDecimal result = num.setScale(2, BigDecimal.ROUND_HALF_UP); System.out.println(result); // 输出:123.46compareTo,禁止用equals)equals:既比较值,又比较精度(0.1和0.10判定为不相等);compareTo:仅比较数值大小(推荐,返回值规则同 BigInteger)。public static void main(String[] args) { BigDecimal d1 = new BigDecimal("0.1"); BigDecimal d2 = new BigDecimal("0.10"); // ❌ 错误:equals比较精度,返回false System.out.println(d1.equals(d2)); // false // ✅ 正确:compareTo仅比较数值,返回0(相等) System.out.println(d1.compareTo(d2) == 0); // true // 大于/小于判断 boolean gt = new BigDecimal("1.2").compareTo(new BigDecimal("1.1")) > 0; // true boolean lt = new BigDecimal("1.2").compareTo(new BigDecimal("1.3")) < 0; // true }BigDecimal big = new BigDecimal("100.99"); int intVal = big.intValue(); // 转int(截断小数)→ 100 long longVal = big.longValue(); // 转long →100 double doubleVal = big.doubleValue(); // 转double(可能丢失精度)→100.99| 维度 | BigInteger | BigDecimal |
|---|---|---|
| 处理数据类型 | 纯整数(无小数) | 整数 + 小数(任意精度) |
| 核心优势 | 超大整数无限制运算 | 精准小数运算、解决浮点丢失 |
| 关键参数 | 无精度参数 | 需指定精度+舍入模式 |
| 核心场景 | 密码学、大数幂运算、进制转换 | 金融金额、高精度小数计算 |
| 特殊能力 | 素数检测、最大公约数 | 小数格式化、精度控制 |
✅选型口诀:
long→ 用BigInteger;BigDecimal;int/long。double构造 BigDecimal→ ✅ 解决:统一用字符串构造new BigDecimal("xxx")。
→ ✅ 解决:除法必须传 3 个参数divide(除数, 保留位数, 舍入模式)。
equals比较 BigDecimal 大小→ ✅ 解决:用compareTo方法比较,a.compareTo(b) == 0表示相等。
→ ✅ 解决:大数运算后必须接收返回值,如BigInteger c = a.add(b),而非直接a.add(b)。
→ ✅ 解决:int/long/float/double能满足的场景,优先用基本类型(性能更高)。
BigInteger base = BigInteger.valueOf(100); BigInteger result = base.pow(20); System.out.println("100^20 = " + result); // 输出:10000000000000000000000000000000000000000000000000public static void main(String[] args) { // 订单原价、折扣、税费(均用字符串构造) BigDecimal price = new BigDecimal("999.99"); BigDecimal discount = new BigDecimal("50.5"); BigDecimal tax = new BigDecimal("23.68"); // 计算最终金额:原价 - 折扣 + 税费,保留2位小数,四舍五入 BigDecimal finalPrice = price.subtract(discount).add(tax) .setScale(2, BigDecimal.ROUND_HALF_UP); System.out.println("最终订单金额:¥" + finalPrice); // 输出:¥973.17 }java.math包的BigInteger(超大整数)和BigDecimal(高精度小数);