58

我知道 (-0 === 0) 是真的。我很想知道为什么 -0 < 0 会发生?

当我在 stackoverflow 执行上下文中运行此代码时,它返回0.

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

但是当我在浏览器控制台中运行相同的代码时,它会返回-0. 这是为什么?我试图在谷歌上搜索它,但没有找到任何有用的东西。这个问题可能不会为某个实际示例增加价值,我想了解 JS 是如何计算它的。

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

4 回答 4

60

-0不小于0or +0,两者都-0 < 0返回-0 < +0False您将 的行为与与/Math.min的比较混合在一起。-00+0

规范Math.min在这一点上很清楚:

湾。如果数字为-0,最低为+0,则将最低设置为-0。

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

注意:这个例外已经出现在1997 规范Math.min(x, y),所以这不是后来添加的内容。

于 2021-12-22T14:17:38.273 回答
12

这是 的 特长Math.min,如 指定:

21.3.2.25 Math.min ( ...args )

[...]

  1. 对于每个强制的元素数,执行

一个。如果数字为 NaN,则返回 NaN。

湾。如果数字为-0,最低为+0,则将最低设置为-0。

C。如果数字 < 最低,则将最低设置为数字。

  1. 返回最低。

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

于 2021-12-22T14:17:28.150 回答
9

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

我很想知道为什么 -0 < 0 会发生?

它不是真的;<是与“最小值”分开的操作,并且Math.min不仅仅基于 IEEE<比较,例如b<a ? b : a.

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

JS 语言的设计者想要Math.min成为 NaN 传播,所以仅仅基于它<是不可能的。 他们选择使其完全可交换,包括有符号零,这似乎是一个明智的决定。

OTOH,大多数代码都不关心有符号零,因此这种语言设计选择会为每个人花费一些性能,以迎合某些人想要定义良好的有符号零语义的罕见情况。

如果您想要一个忽略数组中 NaN 的简单操作,请使用current_min = x < current_min ? x : current_min. 这将忽略所有 NaN,也忽略-0current_min <= +0.0IEEE 比较)。或者如果current_min开始是 NaN,它将保持 NaN。其中许多事情对于Math.min函数来说是不可取的,所以它不能那样工作。


如果你比较其他语言C 标准fmin函数是可交换的。NaN(如果有,则返回非 NaN,与 JS 相反),但不需要是可交换的。签名为零。一些 C 实现选择像 JS for +-0.0 for fmin/一样工作fmax

但是C++std::min 纯粹是根据操作定义的<所以它确实以这种方式工作。(它旨在通用地工作,包括像字符串这样的非数字类型;不像std::fmin它没有任何特定于 FP 的规则。)请参阅在 x86 上提供无分支 FP min 和 max 的指令是什么?回复:x86 的minps指令和 C++std::min都是不可交换的。NaN 并签名为零。


IEEE 754<不会为您提供不同 FP 编号的总顺序。Math.min除了 NaN(例如,如果你用它和Math.max.Math.max输入数组。

Math.min如果没有类似查看它返回哪个 arg 的东西,单独进行排序是不够的==,但是对于有符号零和 NaN 来说,这会分解。

于 2021-12-23T03:57:55.377 回答
0

奇怪的是,规范是矛盾的。比较<规则明确表示-0不小于+0。但是,规范的Math.min()说法相反:如果当前(在迭代参数时)值为-0,并且到目前为止的最小值为+0,则最小值应设置为-0

我希望有人为此激活 TJ Crowder 信号。

编辑——在一些评论中建议,该行为的一个可能原因是可以检测到一个-0值,即使对于普通表达式中的几乎所有目的,它-0都被视为 plain 0

于 2021-12-22T14:17:35.080 回答