繁体   English   中英

为什么 Java 的 double/float Math.min() 以这种方式实现?

[英]Why is Java's double/float Math.min() implemented this way?

我在java.lang.Math的源代码中查看了一些东西,我注意到虽然Math.min(int, int) (或其长对应物)是以这种方式实现的:

public static int min(int a, int b) {
   return a <= b ? a : b;
}

这对我来说完全有意义,这和我会做的一样。 但是,双/浮点实现是这样的:

public static float min(float a, float b) {
   if (a != a) {
      return a;
   } else if (a == 0.0F && b == 0.0F && (long)Float.floatToRawIntBits(b) == negativeZeroFloatBits) {
      return b;
   } else {
      return a <= b ? a : b;
   }
}

我完全傻眼了。 和自己a 第二次检查是为了什么? 为什么它不以与 int/long 版本相同的方式实现?

浮点数比 integer 值复杂得多。

对于这种特定情况,有两个区别很重要:

  • NaNfloatdouble的有效值,它表示“不是数字”并且行为怪异。 即,它不等于自身。
  • 浮点数可以区分 0.0 和 -0.0。 当您计算某些 function 的限制时,负零可能很有用。 区分极限是从正方向还是负方向接近 0 可能是有益的。

所以这部分:

if (a != a) {
      return a;
}

确保在aNaN时返回NaN (如果a不是NaN ,但b是,那么稍后的“正常”检查将返回b ,即NaN ,因此这种情况不需要显式检查)。 这是一种常见模式:当计算一个输入为NaN的任何内容时,output 也将是NaN 由于NaN通常表示计算中的一些错误(例如将 0 除以 0),因此重要的是它会“毒化”所有进一步的计算以确保错误不会被无声地吞噬。

这部分:

if (a == 0.0F && b == 0.0F && (long)Float.floatToRawIntBits(b) == negativeZeroFloatBits) {
      return b;
}

确保如果您比较两个零值浮点数并且b为负零,则返回负零(因为 -0.0“小于”0.0)。 NaN类似,如果 a 为 -0.0 且b为 0.0,则正常检查将正确返回a

我建议仔细阅读Math.min的文档以及浮点数比较运算符 他们的行为完全不同。

Math.min的相关部分:

如果任一值为 NaN,则结果为 NaN。 与数值比较运算符不同,此方法认为负零严格小于正零。

并来自 JLS §15.20.1“数值比较运算符 <、<=、> 和 >=”

由 IEEE 754 标准的规范确定的浮点比较的结果是:

  • 如果任一操作数为 NaN,则结果为假。

  • 正零和负零被认为是相等的。

如果任何参数为 NaN, Math.min将选择该参数,但如果任何操作数为 NaN,则<=计算结果为false 这就是为什么它必须检查a是否不等于自身 - 这意味着a是 NaN。 如果a不是 NaN 但b是,则最后一种情况将涵盖它。

Math.min还认为-0.0是“小于” +0.0 ,但数字比较运算符认为它们是相等的。 这是第二次检查的目的。

为了完整/清晰起见,让我们绘制一张所有可能结果的表格:

  • ab中的任何一个都可以是

    • ,
    • -0 ,
    • 0(即+0),或
    • 其他一些非 NaN 非零值,标记为“(其他)”。

    为了完整起见,写出所有这些组合,并在某些情况下区分正数和负数以清楚起见,给出了下表中的 20 行,尽管它们中的大多数都很简单且没有问题。

  • 标题为“Correct min”的列是应该根据IEEE 754 标准Math.min的 Java 文档返回的正确值,标题为“Naive min”的列是如果 Math.min 将返回的值Math.min已被实施为return a <= b? a: b; return a <= b? a: b; 反而。

一个 b 正确的最小值 天真分钟 关于朴素分钟的注意事项 天真敏错了?
b,因为 NaN 比较给出错误。
-0 -0 b,因为 NaN 比较给出错误。 错误的
0 0 b,因为 NaN 比较给出错误。 错误的
(其他) (其他) b,因为 NaN 比较给出错误。 错误的
-0 b,因为 NaN 比较给出错误。
-0 -0 -0 -0 a,为 -0 ≤ -0。
-0 0 -0 -0 a,为 -0 ≤ 0。
-0 (其他>0) -0 -0
-0 (其他<0) (其他<0) (其他<0)
0 b,因为 NaN 比较给出错误。
0 -0 -0 0 a,按照 IEEE 754 为“0 ≤ -0”。 错误的
0 0 0 0 a,当 0 ≤ 0。
0 (其他>0) 0 0
0 (其他<0) (其他<0) (其他<0)
(其他) b,因为 NaN 比较给出错误。
(其他<0) -0 (其他<0) (其他<0)
(其他>0) -0 -0 -0
(其他<0) 0 (其他<0) (其他<0)
(其他>0) 0 0 0
(其他) (其他) (其他) (其他)

[“正确最小值”和“朴素最小值”最后一行中的“(其他)”表示正确的最小值,在直接意义上,不会因为 NaN 或 -0 而造成任何混淆。]

因此,您会看到上表中有四行,其中天真的 function 会给出错误的答案:

  • 其中三个是当a是 NaN 而b不是时的情况。 这就是 function 中的第一个检查的用途。

  • 另一种情况是Math.min(0, -0)由 Java 记录为返回 -0,即使 IEEE 754 将 0 和 -0 视为相等进行比较(因此比较“0 ≤ -0”评估为真的)。 这就是 function 中的第二次检查的用途。

if (a != a) ,我可以帮助您进行第一次比较。 这显然只看a ,那么在哪些情况下a可能是最小值而不管b

floatint的不同之处在于具有特殊值,例如NAN NAN的一个特殊属性是比较总是错误的。 因此,如果每个比较运算符在a a

b的相同条件可以在最后一行中找到。 如果对b的比较总是返回 false,则最后一行总是返回b

在第二个条件下,我只能猜测这与“负零”和“正零”有关,另外两个特殊值float 当然,负零小于正零。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM