Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

你知道+0与-0的区别吗? #10

Open
akira-cn opened this issue Aug 2, 2019 · 2 comments
Open

你知道+0与-0的区别吗? #10

akira-cn opened this issue Aug 2, 2019 · 2 comments

Comments

@akira-cn
Copy link
Owner

akira-cn commented Aug 2, 2019

JavaScript的数值Number用64位的浮点数表示,首位是符号位,然后是52位的整数位和11位的小数位。如果符号位为1,其他各位均为0,那么这个数值会被表示成“-0”。

所以JavaScript的“0”值有两个,+0和-0。

const num = -0;
console.log(num); // -0

我们可以用二进制位构造出-0来:

// 首先创建一个8位的ArrayBuffer
const buffer = new ArrayBuffer(8);
// 创建DataView对象操作buffer
const dataView = new DataView(buffer);

// 将第1个字节设置为0x80,即最高位为1
dataView.setUint8(0, 0x80);

// 将buffer内容当做Float64类型返回
console.log(dataView.getFloat64(0)); // -0

在一般的运算中,我们也有可能产生-0值,比如运算时将-Infinity作为分母,负数除法超过最小可表示数等等:

console.log(1 / -Infinity); // -0
console.log(-1 / Infinity); // -0
console.log(-Number.MIN_VALUE / 2); // -0
console.log(-1e-1000); // -0

一般情况下-0视同于0,运算并没有什么问题:

console.log(-0 === 0); // true

const num = -0;
if(!num) { // !num 为 true
  // do sth.
}

除非在分母中有-0存在,恰好又要判断符号的时候,可能会出问题:

const num = -0;

console.log(1 / num > 0); // false
console.log(Math.sign(1 / num)); // -1

所以如果你知道分母中可能出现0的时候,就要小心-0。

-0也有好处,我们可以利用-0来表示状态,比如我们在寻路的时候,把地图的障碍物用1表示,空地用0表示,那么我们就可以把未搜索过的用-1和-0表示,这样也挺方便的。

另外比如我们要存mousemove的坐标位置,那么当鼠标移动出检测范围的时候,可以把mousePosition设为-0来表示当前鼠标不在范围内。

如果要使用-0,我们就得区分它与0。

num === -0是不行的,因为0 === -0也会返回true。

用Math.sign也是不行的,Math.sign一共会返回4种值:

console.log(Math.sign(0)); // 0
console.log(Math.sign(-0)); // -0
console.log(Math.sign(0.01); // 1
console.log(Math.sign(-0.01); // -1

实际上在ES2015中提供了Object.is方法,可以判断-0.

👉🏻 【冷知识】Object.is(value1, value2) 比较两个值是否相同。它与=== 的区别是,Object.is(-0, 0)返回false,Object.is(NaN, NaN)返回true。

如果在早期的JavaScript版本中,我们也可以利用1/-0为-Infinity的特点来判断:

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

或者我们可以写一个Object.is的polyfill:

if (!Object.is) {
  Object.is = function(x, y) {
    // SameValue algorithm
    if (x === y) { // Steps 1-5, 7-10
      // Steps 6.b-6.e: +0 != -0
      return x !== 0 || 1 / x === 1 / y;
    } else {
      // Step 6.a: NaN == NaN
      return x !== x && y !== y;
    }
  };
}

好了,关于-0的问题,你还有什么想法,欢迎在issue中讨论。

@hax
Copy link

hax commented Aug 19, 2019

补充,所有位运算都会把 -0 转为 0。因为位运算首先会转成int32,而int32是没有 -0的。另外bigint里也是没有-0的,所以Object.is(0n, -0n)返回true。

再补充一个序列化有关的坑:JSON.parse("-0") 返回 -0 然而 JSON.stringify(-0) 返回 "0",所以是不对称的。😓

@333van
Copy link

333van commented Jun 5, 2024

”首位是符号位,然后是52位的整数位和11位的小数位“,有误吧。

首位是符号位,后面11位是指数,最后52位是尾数。

参考:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants