JDK 17 默认启用严格浮点语义(JEP 306),所有浮点运算均强制遵循 IEEE 754 标准,strictfp 关键字仍保留但无实际效果,跨平台计算结果完全一致。

JDK 17 不再需要 strictfp 关键字——它已退场,不是被废弃,而是被“默认化”了。所有浮点运算现在都等效于加了 strictfp,这是 JEP 306 的核心事实。
为什么 Java 17 突然“取消” strictfp 关键字
这不是语言设计的倒退,而是硬件演进的结果。20 多年前,x86 架构的 x87 浮点协处理器使用 80 位扩展精度做中间计算,导致同一段 Java 代码在不同平台(比如 x86 vs SPARC)上输出微小差异。为保证可移植性,Java 引入 strictfp 强制截断到 IEEE 754 规定的 64 位(double)或 32 位(float)精度。
但自 SSE2 指令集普及(Pentium 4 起),现代 CPU 默认用 64 位寄存器执行 double 运算,不再隐式提升中间精度。x87 模式基本退出主流。JDK 17 借此契机,把原本需显式声明的严格语义,变成 JVM 的唯一行为:
-
strictfp仍合法,但编译器忽略它(无任何效果) - 所有
double/float运算、Math方法调用、甚至字节码级dadd/fmul都强制遵循 IEEE 754 二进制双精度/单精度语义 - 禁用任何硬件级中间精度扩展(如 x87 的 80 位、ARM 的 128 位 FP 扩展)
如何验证你的 CPU + JVM 确实执行了严格浮点
最直接的方式是复现经典陷阱:检查 0.1 + 0.2 == 0.3 的布尔结果,以及打印实际值。在 JDK 17 上你将看到:
System.out.println(0.1 + 0.2); // 输出 0.30000000000000004(和 JDK 11 相同) System.out.println(0.1 + 0.2 == 0.3); // false(始终为 false,与版本无关)
注意:这个输出值本身没变,变的是**计算路径的确定性**。关键验证点在于跨平台一致性:
- 在 x86-64 Linux / AArch64 macOS / x86-64 Windows 上运行同一 jar 包,
Math.sin(1.23456789)返回完全相同的double位模式(可用Double.doubleToLongBits()比对) - 禁用 JVM 的
-XX:+UseX87(该参数在 JDK 17 已移除),说明底层不再保留旧路径开关 - 若你仍在用老旧内核或定制 JVM(如某些嵌入式 OpenJDK 移植版),需确认其是否完整实现 JEP 306 —— 并非所有 vendor build 都同步
strictfp 消失后,数值敏感代码要改什么
绝大多数业务代码无需修改,但三类场景必须重新审视:
- 依赖历史“宽松语义”做近似容错的代码(例如用
==比较浮点结果,且之前靠 x87 高精度“偶然对齐”) - 与 C/C++ 库通过 JNI 交互时,若 C 侧未显式用
FLT_EVAL_METHOD=0或#pragma STDC FENV_ACCESS(ON)控制求值精度,可能出现 Java 侧更“标准”、C 侧更“宽松”的结果偏差 - 单元测试里硬编码了浮点输出字符串(如
assertEquals("0.30000000000000004", String.valueOf(x)))——JDK 17 下可能因舍入路径微调而失败
真正容易被忽略的,是那些没写过 strictfp 却长期受益于它的人:科学计算库(如 Apache Commons Math)、金融风控引擎、物理仿真模块。它们过去靠 strictfp 类/方法兜底,现在反而要检查是否因默认严格化引入了意料外的性能回退(尽管极少,但高密度向量运算中,失去硬件扩展精度可能增加指令数)。

















