Skip to content

二进制

0.1 + 0.2 !== 0.3

计算时,数据转换成二进制再计算,计算结果再转换成十进制。0.1 和 0.2 的二进制都是以 1100 无限循环的小数,二进制计算发生了精度丢失,再转换成十进制后结果就不一致了。

js
0.1 + 0.2; // 0.30000000000000004
0.1 - 0.3; // -0.19999999999999998
0.1111 + 0.2222; // 0.33330000000000004

判断 0.1 + 0.2 === 0.3

Number.EPSILON 表示 JavaScript 的最小精度。误差如果小于这个值,就可以认为是“相等”了。

js
function mathEqual(a, b) {
  return Math.abs(a - b) < Number.EPSILON; 
}

mathEqual(0.1 + 0.2, 0.3); // true
mathEqual(0.1 - 0.3, -0.2); // true
mathEqual(0.1111 + 0.2222, 0.3333); // true

位运算符

对二进制数据进行的运算加减乘除等都是叫位运算,即将符号位共同参与运算的运算。

运算符描述运算规则
&两个位都为 1 时,结果才为 1
|两个位都为 0 时,结果才为 0
^异或两个位相同为0,相异为1
~取反0变1,1变0
<<左移各二进制位全部左移若干位,高位丢弃,低位补0
>>右移各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃

与 AND(&)

js
0 & 0; // 0  
0 & 1; // 0  
1 & 0; // 0  
1 & 1; // 1

两位同时为1,结果才为1,否则结果为0。

js
3 & 5; // 1
0b00000011 & 0b00000101; // 1 
//   0000 0011
//   0000 0101
// = 0000 0001

判断奇偶

二进制最未位为 0 就是偶数,为 1 就是奇数。

js
1 & 1 === 1; // 奇数
2 & 1 === 0; // 偶数

小提示

一般情况下,判断速度略慢于 n % 2 === 0;

或 OR(|)

js
0 | 0; // 0
0 | 1; // 1  
1 | 0; // 1  
1 | 1; // 1

只要有一个为1,其值为1。

js
3 | 5; // 7
0b00000011 | 0b00000101; // 7
//   0000 0011
//   0000 0101
// = 0000 0111

异或 XOR(^)

js
0 ^ 0; // 0  
0 ^ 1; // 1  
1 ^ 0; // 1  
1 ^ 1; // 0
js
3 ^ 5; // 6
0b00000011 ^ 0b00000101; // 6
//   0000 0011
//   0000 0101 
// = 0000 0110

取反(~)

js
~6; // -7
~0b00000110; // -7
//   0000 0110
// = 0000 0110 // 求补码
// = 1111 1001 // 按位取反
// = 1000 0111 // 求补码

~-7; // 6
~-0b00000111; // 6
//   1000 0111
// = 1111 1001 // 求补码
// = 0000 0110 // 按位取反
// = 0000 0110 // 求补码

浮点数转整数

js
~~3.14; //3

左移(<<)

符号位不变,左边二进制位丢弃,右边补0。 每左移一位相当于该数乘以2。

js
1 << 2; // 4
//   0000 0001
// = 0000 0100

-1 << 2; // -4
//   1000 0001
// = 1000 0100

右移(>>)

符号位不变,二进制位右边丢弃,正数左补0,负数左补1。 每右移一位相当于该数除以2。

js
100 >> 2; // 25
//   0110 0100
// = 0001 1001

交换律

js
(a ^ b) ^ c === a ^ (b ^ c);

结合律

js
(a + b) ^ c === a ^ b + b ^ c;

对任何数,都有 x ^ x === 0;x ^ 0 === x;

自反性

js
a ^ b ^ b === a ^ 0 === a;

算法题:136. 只出现一次的数字

给你一个非空整数数组nums,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

示例:

js
输入:nums = [4,1,2,1,2]
输出:4

解题思路

  • 位运算中,对任何数,都有 x ^ x === 0;x ^ 0 === x;
    • 2 个相同数字进行异或运算,结果为 0。
    • 任何数字和 0 进行异或运算,结果为数字本身。
  • 交换律 (a ^ b) ^ c === a ^ (b ^ c);,异或计算顺序不影响最终结果。

所以,遍历数组进行异或计算,重复的数字计算后为 0,最后剩下唯一一个不重复数字。

js
0 ^ 4; // 4
2 ^ 2; // 0
1 ^ 1; // 0
参考答案
ts
function singleNumber(nums: number[]): number {
  let ans = 0;
  for (let i = 0; i < nums.length; ++i) {
    ans ^= nums[i];
  }
  return ans;
};

原码、补码、反码

有符号数三种表示:原码、反码、补码

原码:该数的二进制数。

反码:

  • 正数的反码与原码相同
  • 负数的反码为除符号位,按位取反

补码:

  • 正数补码与原码相同
  • 负数补码为反码加1