+0和-0相同吗?

224

阅读ECMAScript 5.1规范,可以发现+0-0是有区别的。

那么为什么+0 === -0的结果会是true呢?


可能是区分+0和-0的重复问题。 - GolezTrol
9
请注意,在ES2015中,您可以使用Object.is来区分+0和-0。 - Benjamin Gruenbaum
1
引用《JavaScript权威指南》中的David Flanagan所说:当数值运算结果比最小可表示数字还要接近于零时,就会发生下溢。此时,JavaScript会返回0。如果下溢发生在负数上,JavaScript会返回一个特殊的值,即“负零”。 - RBT
11个回答

237
JavaScript使用IEEE 754标准来表示数字。来自Wikipedia

有符号零是带有符号的零。在普通算术中,-0 = +0 = 0。然而,在计算中,一些数字表示允许存在两个零,通常用-0(负零)+0(正零)表示。这在整数的一些有符号数字表示法和大多数浮点数表示法中发生。数字0通常被编码为+0,但可以由+0或-0表示。

浮点运算的IEEE 754标准(目前由大多数支持浮点数的计算机和编程语言使用)要求同时具有+0和-0。可以将这些零视为扩展实数线的变体,使得1 / -0 = -∞和1 / +0 = +∞,仅对±0 / ±0和±∞ / ±∞的除法未定义。

文章包含了关于不同表示法的更多信息。
因此,这就是为什么从技术上讲,必须区分两个零的原因。

然而,+0 === -0的结果为true。为什么会这样(...)?

这种行为在第11.9.6节中明确定义了严格相等比较算法(部分强调是我的):

比较x === y,其中xy是值,生成truefalse。执行此类比较的方法如下:

(...)

  • 如果Type(x)是Number,则

    1. 如果x是NaN,则返回false。
    2. 如果y是NaN,则返回false。
    3. 如果x与y是相同的Number值,则返回true。
    4. 如果x是+0且y是-0,则返回true。
    5. 如果x是-0且y是+0,则返回true。
    6. 返回false。

(...)

(对于+0 == -0也是一样的。)

+0-0视为相等似乎是合理的。否则我们将不得不在代码中考虑这一点,而我个人不想这样做;)


注意:

ES2015引入了一种新的比较方法,Object.isObject.is明确区分-0+0

Object.is(-0, +0); // false

23
实际上,1/0 === Infinity; // true1/-0 === -Infinity; // true - user113716
60
所以我们有 1 === 1+0 === -0,但是 1/+0 !== 1/-0。多么奇怪! - Randomblue
10
我认为这肯定比+0 !== -0好;) 这可能会产生问题。 - Felix Kling
@FelixKling,或者0 !== +0 / 0 !== -0,这也会导致问题! - Yanick Rochon
8
实际上,这种行为在数学中用于模拟极限计算。例如,函数1/x在0处的值为无穷大,但是如果我们从正侧或负侧接近0,则会存在不同的结果;从正侧接近时,结果为正无穷大,而从负侧接近时则为负无穷大。 - Agoston Horvath
显示剩余4条评论

28

因为我忽略了@user113716的评论,我会将其作为回答添加。

您可以通过执行以下操作来测试-0:

function isMinusZero(value) {
  return 1/value === -Infinity;
}

isMinusZero(0); // false
isMinusZero(-0); // true

10
最好也检查一下是否等于0,因为上面的isMinusZero(-1e-323)返回true! - Chris
2
@Chris,双精度指数的极限是e±308,你的数字只能以非规格化形式表示,并且不同的实现对于是否支持它们在哪里支持它们有不同的意见。关键是,在某些机器上,在某些浮点模式下,你的数字被表示为-0,而在其他机器上则表示为非规格化数字0.000000000000001e-308。这样的浮点数,真是太有趣了。 - weaknespase
这可能对其他语言也适用(我测试了C,它可以工作) - Mukul Kumar

27

我刚刚遇到了一个例子,在这个例子中,+0和-0的行为确实非常不同:

Math.atan2(0, 0);  //returns 0
Math.atan2(0, -0); //returns Pi

注意:即使在负数(如-0.0001)上使用Math.round,实际上会变成-0,并可能会损坏一些后续的计算,就像上面所示。

修复这个问题的快速而不太优雅的方法是执行以下操作:

if (x==0) x=0;

或者仅仅:

x+=0;

如果原数是负零,这将其转换为正零。


2
谢谢。加零竟然解决了我遇到的问题,这真是太奇怪了。“如果其他方法都不行,就加零。” 这是一个人生的教训。 - Microsis
1
我在Math.atan(y/x)中也遇到了这个问题,它(也许令人惊讶地)可以处理正无穷或负无穷的"y/x",但在x为-0的情况下会给出错误的答案。将"x"替换为"(x+0)"即可解决此问题。 - Jacob C.

9

2021的答案

+0和-0相同吗?

简短回答: 取决于您使用的比较运算符。

详细回答:

基本上,我们到目前为止有4种比较类型

  1. “宽松”的等式
console.log(+0 == -0); // true
  • 严格相等('strict' equality)
  • console.log(+0 === -0); // true
    
    1. ‘Same-value’ 相等性ES2015 的 Object.is
    console.log(Object.is(+0, -0)); // false
    
    1. ‘同值零’ 相等性ES2016
    console.log([+0].includes(-0)); // true
    

    As a result, just Object.is(+0, -0) makes difference with the other ones.

    const x = +0, y = -0; // true                -> using ‘loose’ equality
    console.log(x === y); // true                -> using ‘strict’ equality
    console.log([x].indexOf(y)); // 0 (true)     -> using ‘strict’ equality
    console.log(Object.is(x, y)); // false       -> using ‘Same-value’ equality
    console.log([x].includes(y)); // true        -> using ‘Same-value-zero’ equality

    enter image description here


    8
    在JavaScript中,用IEEE 754标准表示数字类型,符号由一个二进制位表示(1表示负数)。
    因此,每个可表示的数字都有正负两个值,包括0。
    这就是为什么会同时存在-0+0的原因。

    3
    二进制补码也使用一位来表示符号,但只有一个零(表示正数)。 - Felix Kling
    4
    在二进制补码中,负数位也是值的一部分,因此一旦设置了负数位,它就不再是零了。 - Arnaud Le Blanc

    4
    我会把它归咎于严格相等比较方法('===')。请查看第4d节enter image description here
    请看规范中的7.2.13严格相等比较 章节

    4

    回答原标题+0和-0是否相同?:

    Spudley的答案评论中,brainslugs83指出了一个重要的情况,在JavaScript中,+0和-0不相同 - 它们被实现为函数:

    var sign = function(x) {
        return 1 / x === 1 / Math.abs(x);
    }
    

    除了标准的Math.sign之外,这将返回+0和-0的正确符号。


    你的函数应该返回 true 或者 false,而不是 -11 - Eugene Mala

    3
    我们可以使用Object.is来区分+0和-0,还有一件事,NaN==NaN
    Object.is(+0,-0) //false
    
    Object.is(NaN,NaN) //true
    

    3
    如果您需要支持 -0+0sign 函数:
    var sign = x => 1/x > 0 ? +1 : -1;
    

    它与Math.sign类似,但sign(0)返回1,而sign(-0)返回-1


    sign(Infinity) 返回 -1 - undefined

    2

    0有两个可能的值(位表示),这并不是唯一的。特别是在浮点数中,这种情况经常发生。因为浮点数实际上是以一种公式的形式存储的。

    整数也可以以不同的方式存储。您可以具有带有额外符号位的数字值,因此在16位空间中,您可以存储15位整数值和一个符号位。在此表示中,值1000(十六进制)和0000都是0,但其中一个是+0,另一个是-0。

    这可以通过从整数值中减去1来避免,使其范围从-1到-2 ^ 16,但这将很不方便。

    更常见的方法是使用“二补码”存储整数,但显然ECMAscript选择不这样做。在这种方法中,数字范围从0000到7FFF正数。负数从FFFF(-1)到8000开始。

    当然,相同的规则也适用于更大的整数,但我不想让我的F磨损。;)


    4
    你不觉得 +0 === -0 有点奇怪吗?因为现在我们有了 1 === 1+0 === -0,但是 1/+0 !== 1/-0... - Randomblue
    3
    当然,+0和-0是一样的,都代表着零。但是+无穷大和-无穷大之间有很大的区别,对吗?这些无穷大的数可能是ECMA支持+0和-1的原因。 - GolezTrol
    2
    +0 等于 -0,都是 0,什么也没有。它们相同是有道理的。为什么天空是蓝色的?4+3 和 1+6 相同,尽管表示不同。它们有不同的表示(因此具有不同的位值),但在比较时,它们被处理为相同的零,因为它们确实是相同的。 - GolezTrol
    1
    它们不是相同的。请参见https://dev59.com/c2w05IYBdhLWcg3weBru,以查看示例。 - Randomblue
    这只是一个hack,为了避免JavaScript程序员在遇到它时感到困惑。-0甚至被打印并转换为字符串"0"--仅仅抛弃一个数字的符号非常糟糕。至于“你没有解释为什么0和-0不相等”的评论--他不必这样做,因为在IEEE 754中已经定义为不同。这是一个国际计算机工程标准。 - BrainSlugs83
    显示剩余2条评论

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接