Android与Kotlin临时文件中DecimalFormat处理末位为5的小数结果不一致
核心原因
浮点数精度本质问题
double类型无法精确存储十进制小数(比如10.495),实际存储的是二进制近似值。Android ART虚拟机和桌面JVM对这些近似值的二进制表示可能存在细微差异:比如10.495在Android上的实际存储值可能略大于10.495,而桌面JVM上略小于,导致HALF_EVEN舍入时触发不同的判断逻辑,最终结果不同。DecimalFormat底层实现差异
Android的DecimalFormat基于ICU(International Components for Unicode)库实现,而桌面JVM的DecimalFormat是JDK原生实现。两者即使都遵循HALF_EVEN舍入规则,对浮点数近似值的处理细节也存在区别,这是跨平台结果不一致的直接原因。JDK修复未同步到Android
你提到的JDK相关Bug虽已标记解决,但Android ART并未完全同步JDK的所有底层实现变更,因此修复逻辑无法在Android端生效。
解决方案:确保跨平台一致的舍入方式
要解决这个问题,核心是避免依赖double的近似值进行舍入,改用精确的十进制数值处理:
方案1:使用BigDecimal精确构造数值
不要直接用double类型初始化BigDecimal,而是通过字符串或BigDecimal.valueOf(double)来构造,这样能规避double的精度误差:
方式A:从字符串构造(推荐,完全精确)
import java.math.BigDecimal import java.math.RoundingMode fun formatNumber(numStr: String): String { return BigDecimal(numStr) .setScale(2, RoundingMode.HALF_EVEN) .toPlainString() }
方式B:从double转换(需用valueOf)
如果必须从double输入,使用BigDecimal.valueOf(double)而非直接BigDecimal(double),因为valueOf会先将double转换为对应的十进制字符串再构造,避免保留二进制近似误差:
import java.math.BigDecimal import java.math.RoundingMode fun formatNumber(num: Double): String { return BigDecimal.valueOf(num) .setScale(2, RoundingMode.HALF_EVEN) .toPlainString() }
方案2:统一使用ICU库(跨平台对齐)
在桌面JVM项目中引入ICU4J库,让桌面端和Android端使用相同的ICU实现的DecimalFormat,从底层逻辑上统一格式化行为,确保结果一致。
补充说明
你之前尝试BigDecimal无效,大概率是因为直接用BigDecimal(double)构造,这种方式会保留double的二进制近似值,并没有解决精度问题,改用上述两种BigDecimal构造方式即可解决。
内容的提问来源于stack exchange,提问作者NightFury

