二进制
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