JavaScript中的两个“0”（翻译）

JavaScript has two zeros: −0 and +0. This post explains why that is and where it matters in practice.
JavaScript 中有两个“0”： -0 和 +0 。这篇文章解释了为什么，并且指出实际生产中会造成什么影响

1.The signed zero
1.“-0”

Numbers always need to be encoded to be stored digitally. Why do some encodings have two zeros? As an example, let’s look at encoding integers as 4-digit binary numbers, via the sign-and-magnitude method. There, one uses one bit for the sign (0 if positive, 1 if negative) and the remaining bits for the magnitude (absolute value). Therefore, −2 and +2 are encoded as follows.
Binary 1010 is decimal −2
Binary 0010 is decimal +2
Naturally, that means that there will be two zeros: 1000 (−0) and 0000 (+0).

In JavaScript, all numbers are floating point numbers, encoded in double precision according to the IEEE 754 standard for floating point arithmetic. That standard handles the sign in a manner similar to sign-and-magnitude encoding for integers and therefore also has a signed zero. Whenever you represent a number digitally, it can become so small that it is indistinguishable from 0, because the encoding is not precise enough to represent the difference. Then a signed zero allows you to record “from which direction” you approached zero, what sign the number had before it was considered zero. Wikipedia nicely sums up the pros and cons of signed zeros:

It is claimed that the inclusion of signed zero in IEEE 754 makes it much easier to achieve numerical accuracy in some critical problems, in particular when computing with complex elementary functions. On the other hand, the concept of signed zero runs contrary to the general assumption made in most mathematical fields (and in most mathematics courses) that negative zero is the same thing as zero. Representations that allow negative zero can be a source of errors in programs, as software developers do not realize (or may forget) that, while the two zero representations behave as equal under numeric comparisons, they are different bit patterns and yield different results in some operations.

JavaScript goes to some lengths to hide the fact that there are two zeros.
JavaScript 做了很多工作来隐藏有两个“0”的事实。

2.Hiding the zero’s sign
2.隐藏“0”的符号

In JavaScript, you’ll usually write 0, which always means +0. But it also displays −0 simply as 0. The following is what you see when you use any browser command line or the Node.js REPL:

```    > -0
0
```

The reason is that the standard toString() method converts both zeros to the same "0".

```    > (-0).toString()
'0'
> (+0).toString()
'0'
```

The illusion of a single zero is also perpetrated by the equals operators. Even strict equality considers both zeros the same, making it very hard to tell them apart (in the rare case that you want to).

```    > +0 === -0
true
```

The same holds for the less-than and greater-than operators – they consider both zeros equal.

```    > -0 < +0
false
> +0 < -0
false
```

3.Where the zero’s sign matters
3.“0”的符号都影响了哪些地方

The sign of the 0 rarely influences results of computations. And +0 is the most common 0. Only a few operations produce −0, most of them just pass an existing −0 through. This section shows a few examples where the sign matters. For each example, think about whether it could be used to tell −0 and +0 apart, a task that we will tackle in Sect. 4. In order to make the sign of a zero visible, we use the following function.
“-0”在一些很罕见的地方影响计算结果。通常“+0”就是通常的“0”。只有少数运算产生“-0”的结果，大多数都直接忽略“-0”的存在。这一段将展示“-0”在哪些情况下产生影响。想一想下面每一个例子要区别“-0”和“+0”的原因，在展示过程中，为了能清晰的看到“-0”，我们将使用下面这个函数。
```    function signed(x) {
if (x === 0) {
// isNegativeZero() will be shown later 【isNegativeZero() 将在后文中给出实现】
return isNegativeZero(x) ? "-0" : "+0";
} else {
// Otherwise, fall back to the default 【其它情况下，使用默认方法】
// We don’t use x.toString() so that x can be null or undefined 【null 或 undefined 不能使用 x.toString() 的写法】
return Number.prototype.toString.call(x);
}
}
```

3.1.加法

Quoting Sect. 11.6.3 of the ECMAScript 5.1 specification, “Applying the Additive Operators to Numbers”:

The sum of two negative zeros is −0. The sum of two positive zeros, or of two zeros of opposite sign, is +0.

For example:

```    > signed(-0 + -0)
'-0'
> signed(-0 + +0)
'+0'
```

This doesn’t give you a way to distinguish the two zeros, because what comes out is as difficult to distinguish as what goes in.

3.2.Multiplying by zero
3.2.乘法

When multiplying with zero with a finite number, the usual sign rules apply:

```    > signed(+0 * -5)
'-0'
> signed(-0 * -5)
'+0'
```

Multiplying an infinity with a zero results in NaN:

```    > -Infinity * +0
NaN
```

3.3.Dividing by zero
3.3.除法

You can divide any non-zero number (including infinities) by zero. The result is an infinity whose sign is subject to the usual rules.

```    > 5 / +0
Infinity
> 5 / -0
-Infinity
> -Infinity / +0
-Infinity
```

Note that -Infinity and +Infinity can be distinguished via ===.

```    > -Infinity === Infinity
false
```

Dividing a zero by a zero results in NaN: “0”除以“0”为非数字（NaN）。
```    > 0 / 0
NaN
> +0 / -0
NaN
```

3.4.Math.pow()
3.4.乘方运算

The following is a table of the results of Math.pow() if the first argument is zero:

```pow(+0, y<0) → +∞
pow(−0, odd y<0) → −∞ //【奇数次幂】
pow(−0, even y<0) → +∞ //【偶数次幂】
```

Interaction:
```    > Math.pow(+0, -1)
Infinity
> Math.pow(-0, -1)
-Infinity
```

3.5.Math.atan2()
3.5.极坐标弧度

The following is a table of the results that are returned if one of the arguments is zero.

```atan2(+0, +0) → +0
atan2(+0, −0) → +π
atan2(−0, +0) → −0
atan2(−0, −0) → −π

atan2(+0, x<0) → +π
atan2(−0, x<0) → −π
```

Hence, there are several ways to determine the sign of a zero. For example:

```    > Math.atan2(-0, -1)
-3.141592653589793
> Math.atan2(+0, -1)
3.141592653589793
```

atan2 is one of the few operations that produces −0 for non-zero arguments:
atan2 是少数几个能用非零参数产生“-0”的运算之一
```atan2(y>0, +∞) → +0
atan2(y<0, +∞) → −0
```

Therefore: 因此
```    > signed(Math.atan2(-1, Infinity))
'-0'
```

3.6.Math.round()
3.6.四舍五入

Math.round() is another operation that returns −0 for arguments other than −0 and +0:
Math.round()是另一个不用“-0”和“0”能产生“-0”的运算。
```    > signed(Math.round(-0.1))
'-0'
```

Here we have the effect that we talked about at the beginning: The sign of the zero records the sign of the value before rounding, “from which side” we approached 0.

4.Telling the two zeros apart
4.区分两个“0”

The canonical solution for determining the sign of a zero is to divide one by it and then check whether the result is -Infinity or +Infinity:

```    function isNegativeZero(x) {
return x === 0 && (1/x < 0);
}
```

The above sections showed several other options. One original solution comes from Allen Wirfs-Brock. Here is a slightly modified version of it:

```function isNegativeZero(x) {
if (x !== 0) return false;
var obj = {};
Object.defineProperty(obj, 'z', { value: -0, configurable: false });
try {
// Is x different from z’s previous value? Then throw exception.【如果 x 与前面定义的 z 取值不同，则会抛出异常。】
Object.defineProperty(obj, 'z', { value: x });
} catch (e) {
return false
};
return true;
}
```

Explanation: In general, you cannot redefine a non-configurable property – an exception will be thrown. For example:

```    TypeError: Cannot redefine property: z
```

However, JavaScript will ignore your attempt if you use the same value as the existing one. In this case, whether a value is the same is not determined via ===, but via an internal operation that distinguishes −0 and +0. You can read up on the details in Wirfs-Brock’s blog post (freezing an object makes all properties non-configurable).
JavaScript 试图会忽略你对用相同的值进行的修改。在这个例子中，值相同并不是通过全等运算符“===”来判断的，是通过一种内在的机制来区分“-0”和“+0”的。详细内容可以去读 Wirfs-Brock 的博文“设置所有属性为不可配置实现对象锁定（freezing an object makes all properties non-configurable）

5.Conclusion
5.结论

We have seen that there are two zeros, because of how the sign is encoded for JavaScript’s numbers. However, −0 is normally hidden and it’s best to pretend that there is only one zero. Especially, because the difference between the zeros has little bearing on computations. Even strict equality === can’t tell them apart. Should you, against all expectations or just for fun, need to determine the sign of a zero, there are several ways to do so. Note that the slightly quirky existence of two zeros is not JavaScript’s fault, it simply follows the IEEE 754 standard for floating point numbers.

6.扩展阅读

This post is part of a series on JavaScript numbers that comprises the following posts:
• Integers and shift operators in JavaScript
• Displaying numbers in JavaScript
Furthermore, the blog post “Stricter equality in JavaScript” examines that === cannot detect either the value NaN or the sign of a zero.

JavaScript中的两个“0”（翻译）

• 0

开心

• 0

板砖

• 0

感动

• 0

有用

• 0

疑问

• 0

难过

• 0

无聊

• 0

震惊

205 Unobtrusive JavaScript 查看英文原版 译者：darkbaby123 译者前言： Unobtrusive JavaScript这