akira-cn / FE_You_dont_know

分享在前端开发中,你不知道的JavaScript、CSS和HTML趣味知识,增加你的知识面。

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

akira-cn opened this issue · comments

commented

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中讨论。

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

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