为什么 Math.min() return -0 来自 [+0, 0, -0]

Why does Math.min() return -0 from [+0, 0, -0]

我知道 (-0 === 0) 结果是真的。我很好奇为什么会出现 -0 < 0?

当我在 Whosebug 执行上下文中 运行 这段代码时,它 returns 0

const arr = [+0, 0, -0];
console.log(Math.min(...arr));

但是当我 运行 在浏览器控制台中使用相同的代码时,它 returns -0。这是为什么?我试图在 google 上搜索它,但没有找到任何有用的信息。这个问题可能对某些实际例子没有价值,我想了解JS如何计算它。

 const arr = [+0, 0, -0];
    console.log(Math.min(...arr)); // -0

这是Math.min的特色菜,as specified:

21.3.2.25 Math.min ( ...args )

[...]

  1. For each element number of coerced, do

a. If number is NaN, return NaN.

b. If number is -0 and lowest is +0, set lowest to -0.

c. If number < lowest, set lowest to number.

  1. Return lowest.

请注意,在大多数情况下,+0 和 -0 被同等对待,在 ToString 转换中也是如此,因此 (-0).toString() 计算为 "0"。您可以在浏览器控制台中观察到差异是浏览器的实现细节。

规范出奇地矛盾。 < 比较规则明确表示 -0 不小于 +0。然而,Math.min() 的规范说的恰恰相反:如果当前(在遍历参数时)值为 -0,并且到目前为止的最小值是 +0,那么最小值应该设置为 -0.

我希望有人能激活 T.J。这个的克劳德信号。

edit — 在一些评论中有人建议这种行为的一个可能原因是可以检测到 -0 值,即使对于几乎所有在普通表达式中,-0 被视为普通表达式 0.

-0不小于0+0-0 < 0-0 < +0returnsFalse,你'将 Math.min 的行为与 -00/+0 的比较相混合。

specification of Math.min在这一点上很清楚:

b. If number is -0 and lowest is +0, set lowest to -0.

如果没有这个例外,Math.minMath.max 的行为将取决于参数的顺序,这可以被认为是一种奇怪的行为——你可能希望 Math.min(x, y) 总是等于Math.min(y, x) — 所以这可能是一个可能的理由。

注意:此异常已存在于 1997 specificationMath.min(x, y),因此这不是后来添加的内容。

这个答案的重点是解释为什么 Math.min 完全可交换的语言设计选择是有意义的。

I am curious to know why -0 < 0 happens?

不是真的; < 是与“最小值”分开的操作,并且 Math.min 不像 b<a ? b : a.

那样仅基于 IEEE < 比较

那将是不可交换的。 NaN 以及符号零。 (如果任一操作数为 NaN,则 < 为假,因此会产生 a)。
就最小惊喜原则而言,如果 Math.min(-1,NaN)NaNMath.min(NaN, -1)-1.

至少同样令人惊讶(如果不是更多的话)

JS 语言设计者希望 Math.min 是 NaN 传播的,所以仅基于 < 无论如何是不可能的。 他们选择使其完全可交换,包括带符号的零,这似乎是一个明智的决定。

OTOH,大多数代码不关心带符号的零,因此这种语言设计选择对每个人来说都会付出一些性能代价,以迎合某些人想要定义明确的带符号零语义的罕见情况。

如果您想要一个忽略数组中 NaN 的简单操作,请使用 current_min = x < current_min ? x : current_min 迭代自己。这将忽略所有 NaN,并且还会忽略 -0 以获得 current_min <= +0.0(IEEE 比较)。或者,如果 current_min 以 NaN 开头,它将保持为 NaN。对于 Math.min 函数来说,其中许多事情都是不可取的,因此它不会那样工作。


如果你比较其他语言C standard fmin function 是可交换的。 NaN(returning 非 NaN,如果有的话,与 JS 相反),但不需要是可交换的。签名为零。对于 fmin / fmax.

的 +-0.0,一些 C 实现选择像 JS 一样工作

但是 C++ std::min is defined purely in terms of a < operation, so it does work that way. (It's intended to work generically, including on non-numeric types like strings; unlike std::fmin it doesn't have any FP-specific rules.) See 回复:x86 的 minps 指令和 C++ std::min 都是不可交换的。 NaN 和符号零。


IEEE 754 < 不会为您提供不同 FP 编号的总顺序。 Math.min 除了 NaN(例如,如果你用它和 Math.max 构建了一个排序网络)它的顺序与 Math.max 不一致:它们都是 return NaN 如果有的话,因此使用 min/max 比较器的排序网络将生成所有 NaN,如果输入数组中有的话。

Math.min 如果没有 == 之类的东西来查看它 return 编辑了哪个参数,单独使用

Math.min 是不够的,但是对于带符号的零和 NaN 来说,这会分解。