【问题标题】:This very small float constant is more approximate than could be expected这个非常小的浮点常数比预期的更近似
【发布时间】:2026-01-31 21:05:02
【问题描述】:
public class Exponents {
    public static void main(String[] args) {
        float expFloat = 1.38e-43f;                 // 1.38 here
        System.out.println(expFloat);
    }
}

输出:

1.37E-43

这太疯狂了。输出应该是 1.38 ,其他输出工作正常。 1.39 显示 1.39,1.37 显示 1.37,但 1.38 显示 1.37 --- 我的意思是这怎么可能?

更多的实验显示--- 1.47 显示 1.47,1.49 显示 1.49 但 1.48 显示 1.49 。 此外, 1.58 显示 1.58 ,但 1.59 显示 1.58 。

【问题讨论】:

  • 每个人都知道浮点很疯狂,直到人们不再认为它很疯狂。
  • 加倍,浮点数不适合
  • 感谢@DnR 格式化问题。但是你能解释一下为什么会这样吗?
  • 你为什么使用浮点数而不是双精度数?除非您有充分的理由使用 float,否则您应该始终使用 double。您正在使用非规范化数字,其中 float 会失去精度。
  • 你试过1.38e-44f吗?

标签: java floating-point floating-accuracy floating-point-precision


【解决方案1】:

您的号码接近可表示为float 的最小号码。确切地说,它在“非规范化”范围内,因此我们的精度有限。

1.37e-43f1.38e-43f 在内存中都表示为0x00000062。根据 IEEE 754 floats 的定义,这意味着

  • 符号为 0(1 位),因此是 +
  • 指数为 0(8 位),因此非规范化和
  • 尾数为0x62(23位),表示0.000 0000 0000 0000 0110 0010

这意味着我们有很多

(binary) 0.000 0000 0000 0000 0110 0010 * 2 ^ -126
= 0.1100010 * 2 ^ -142
= 1100010 * 2 ^ -149
= (decimal) 98 * 2 ** -149 = 1.3732724950383207e-43
= (binary) 1.100010 * 2 ^ -143 = (hexadecimal) 0x1.88p-143, as used by the C format specifier %a.

如您所见,仅使用尾数中的最低位。

最低位的值为 2 ** -149 = 1.401298464324817e-45。这也是完全可表示的值之间的步骤。

所以,

  • 0x00000062 表示的浮点数具有 f2 值:= 1.3732724950383207e-43 = 0x1.88p-143,
  • 0x00000063 表示的浮点数的值为 f3 := 1.3872854796815689e-43 = 0x1.8cp-143,
  • 0x00000061 表示的浮点数的值为 f1 := 1.3592595103950726e-43 = 0x1.84p-143。

中间没有可能的值,因为我们是非规范化的并且接近精度限制。

根据我们检查的值,

  • 1.36E-43最接近f1,
  • 1.37E-43最接近f2,
  • 1.38E-43 最接近 f2 和
  • 1.39E-43 最接近 f3。

范围 1.47E-43 到 1.49E-43 和 1.57E-43 到 1.59E-43 也是如此。

【讨论】:

  • 这一切的目的也许值得一提。删除最小指数的归一化和前导隐式 1 位允许非常近似地表示微小的数字,否则这些数字会下溢为零。
  • @PatriciaShanahan 没错,但我只是想简要介绍一下这个主题。可以在更合适的地方阅读详细信息。
【解决方案2】:

最小的正浮点值 (Float.MIN_VALUE) 约为 1.4E-45,这意味着 1.3xE-43 中的最后一个数字“x”进入 1.4 的“跳数”,一些数字将被“跳过”。 "

浮点数以二进制而不是十进制表示,并且大多数十进制数都没有精确的表示。例如 0.1 没有精确的表示。如果你在可以表示的幅度的极限附近工作,这个事实就会变得更加明显。

【讨论】:

  • 答案准确,也通俗易懂。