1 はじめに
ご存知のように、ビット演算はコンピュータを学ぶ際に必ず学ばなければならないものです。実際、通常の配列演算は演算を実行する前にバイナリに変換する必要がありますが、ビット演算は直接バイナリ演算を実行します。ビット演算は低レベルの演算です。そのため、速度は (加算、減算、乗算、除算などの他の演算と比較して) 最も速く、一部のアルゴリズムはビット演算を使用して実装できます。演算を適切に使用すると、多くの利点が得られます。先人はバイナリ演算とビット演算を使用して、単純な演算を実行できるコンピュータを提供しましたが、私たちはビット演算に触れることがほとんどありません。すべてのビット演算は 2 進数で実行され、2 進数には 0 と 1 のみが存在します。
以下に、ビット演算の理解を深めるための例をいくつか示します。
バイナリについて
2 進数は、コンピューティング技術で広く使用されている数値体系です。バイナリデータとは、0と1の2桁で表される数値です。基数は 2、キャリー ルールは「2 つごとに 1 になる」、借用ルールは「1 を 2 として借りる」で、18 世紀のドイツの数学哲学者ライプニッツによって発見されました。現在のコンピュータシステムは基本的にバイナリシステムを採用しており、データは主に補数コードの形でコンピュータ内に格納されています。コンピューターのバイナリは非常に小さなスイッチであり、「オン」を使用して 1 を表し、「オフ」を使用して 0 を表します。
2. ビット単位の演算子テーブル
オペレーター | 意味 | 例 | ルール |
---|---|---|---|
& | そして | a&b | 結果が 1 になるのは、両方のビットが 1 の場合のみです。 |
| | または | a|b | 両方のビットが 0 の場合、結果は 0 のみになります。 |
~ | 取反 | ~a | 0は1になり、1は0になります。 |
^ | XOR | a^b | 2 つのビットは 0 に等しく、1 とは異なります。 |
<< | 左方移動 | a<<2 | すべてのバイナリ ビットが数ビット左にシフトされ、上位ビットは破棄され、下位ビットは 0 で埋められます。 |
>> | 右シフト | b>>4 | 2 進数の各ビットは数ビット右にシフトされます。符号なし数値の場合、上位ビットは 0 で埋められ、符号付き数値の場合、右シフトは 1 で埋められます。 |
>>> | 符号なし右シフト | ×>>>2 | すべての 2 進数が数ビット右にシフトされ、上位ビットは 0 で埋められ、下位ビットは破棄されます。 |
2.1 ビット演算 - 論理結果の参照
あ | B | A&B | A|B | A^B |
---|---|---|---|---|
真実 | 真実 | 真実 | 真実 | 間違い |
真実 | 間違い | 間違い | 真実 | 真実 |
間違い | 真実 | 間違い | 真実 | 真実 |
間違い | 間違い | 間違い | 間違い | 間違い |
あ | B | A&B | A|B | A^B | ~A |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 1 |
1 | 0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 1 | 1 | 1 |
1 | 1 | 1 | 1 | 0 | 0 |
2.2 ビット演算 - 例
操作する | 結果 | に相当 | 結果 |
---|---|---|---|
5&1 | 1 | 0101 & 0001 | 0001 |
5 | 1 | 5 | 0101 | 0001 | 0101 |
5^1 | 4 | 0101 ^ 0001 | 0100 |
~5 | 10 | ~0101 | 1010 |
5<<1 | 10 | 0101<<1 | 1010 |
5>>1 | 2 | 0101>>1 | 0010 |
5>>>1 | 2 | 0101>>>1 | 0010 |
以下に、主に AND と OR に注目した例をいくつか示します。
# 例子1
A = 10001001
B = 10010000
A | B = 10011001
# 例子2
A = 10001001
C = 10001000
A | C = 10001001
# 例子1
A = 10001001
B = 10010000
A & B = 10000000
# 例子2
A = 10001001
C = 10001000
A & C = 10001000
2.3 ビット演算の基本
演算子 - 図の例
3. ビット演算の方法にはどのようなものがありますか
3.1. not 演算子を押す ~ もプロジェクトでは非常に便利です。
たとえば、一般的にコードを書く場合、配列に特定の値があるかどうかを判断します。これは次のように判断されます。
let arr = [4,7,10]
if (arr.indexOf(7) > -1) {
console.log('测试')
}//测试
実際には、もっと簡潔に書くことができます。
let arr = [4,7,10]
if (~arr.indexOf(7)) {
console.log('测试')
}
3.2. 左シフト演算子 << を押して 2 の累乗をすばやく取得します
console.log(1 << 5) // 32, 即 2的5次方
console.log(1 << 10) // 1024, 即 2的10次方
// 但是要注意使用
console.log(a = 2e10) // 20000000000
console.log(a << 2) // -1604378624
3.3. ^ を押して変数 0 または 1 を切り替えます
//if 判断
let show;
if (show) {
show = 0;
} else {
show = 1;
}
console.log(show) // 1
//三目运算符
show = show ? 0 : 1;
console.log(show) // 1
//可以换成^
show ^= 1;
console.log(show) //1
## 3.4. & を押してパリティを判定します``` javascript console.log(7 & 1); // 1 console.log(8 & 1) ; // 0 ```
3.5、符号付き左シフト (<<)
符号付き左シフトは、32 ビット 2 進数のすべてのビットを指定されたビット数だけ左にシフトします。のように:
var num = 2; // 二进制10
num = num << 6; // 二进制1000000,十进制128
console.log(num)
//另外
function ceshi(n) {
return 1 << n;
}
console.log(ceshi(5))//32
3.6、符号付き右シフト (>>)
符号付き右シフトは、32 ビット 2 進数のすべてのビットを指定されたビット数だけ右にシフトします。のように:
var num = 64; // 二进制1000000
num = num >> 7; // 二进制10,十进制0
console.log(num)
//另外
var num = 64 >> 1; // 32
console.log(num)
3.7、符号なし右シフト (>>>)
正の数の符号なし右シフトは、符号付き右シフトと同じ結果になります。負の数の符号なし右シフトは符号ビットもシフトし、符号なし右シフトは負の数のバイナリ コードを正の数のバイナリ コードとして扱います。
var num = -64; // 11111111111111111111111111000000
num = num >>> 7; // 33554431
console.log(num)
//另外,我们可以利用无符号右移来判断一个数的正负:
function ceshi(n) {
return (n === (n >>> 0)) ? true : false;
}
console.log(ceshi(-1)) // false
console.log(ceshi(1)) // true
3.8、ビット単位または (|)
| 演算子と || 演算子は、2 つの数値のうちの 1 つが 1、結果が 1、その他が 0 である限り、同じように機能します。
var num = Math.floor(1.3); // 1
console.log(num)//1
//另外
var num = 1.3 | 0; // 1
console.log(num)
他の高度な操作を待ちます。これは例ではありません
4. ビット演算の推論
4.1. ビット単位の OR |
a と b のどちらかが 1 の場合のみ、a|b は 1 になります。
8 | = | 1 | 0 | 0 | 0 |
---|---|---|---|---|---|
2 | = | 0 | 0 | 1 | 0 |
10 | = | 1 | 0 | 1 | 0 |
アプリケーションシナリオ: 丸め
function toInt(num) {
return num | 0
}
console.log(toInt(1.9)) // 1
console.log(toInt(1.32322)) // 1
4.2. ビット単位の AND &
a & b が 1 になるのは、a と b が両方とも 1 の場合のみです。
8 | = | 1 | 0 | 0 | 0 |
---|---|---|---|---|---|
2 | = | 0 | 0 | 1 | 0 |
0 | = | 0 | 0 | 0 | 0 |
応用シナリオ: パリティの判定
var number = 0
console.log(number & 1 === 1)
4.3、ビットごとの XOR ^
2 つのオペランドの対応するビットに 1 が 1 つだけある場合、結果は 1 になり、それ以外の場合は 0 になります。
8 | = | 1 | 0 | 0 | 0 |
---|---|---|---|---|---|
2 | = | 0 | 0 | 1 | 0 |
10 | = | 1 | 0 | 1 | 0 |
アプリケーション シナリオ: 2 つの変数の値を交換する
let a = 5,
b = 6;
console.log(a = a ^ b);//3
console.log(b = a ^ b);//5
console.log(a = a ^ b);//6
// 还可以通过运算
console.log(a = a + b);//11
console.log(b = a - b);//6
console.log(a = a - b);//5
// es 6
console.log([a, b] = [b, a])//[ 6, 5 ]
// 原理剖析:a = a ^ b; b = a ^ b 相当与 b = a ^ b ^ b = a ^ (b ^ b) = a ^ 0 = a;
4.4、ビットごとの NOT ~
簡単に言うと、2進数を反転する、つまり0が1になり、1が0になる(0が1になる、1が0になる)ことです 応用例:ビット演算の演算値は必要なため、小数点以下は四捨五入されます
。整数であり、結果も整数なので、ビット演算の後は自動的に整数になります
4.5,JS如何实现两个大数相加
当有两个整数 a 和 b ,在通常情况下我们有“+”运算符对其进行相加运算:
let sum = a + b;
但是 JS 的Number类型是有内存限制的,数字的长度超过8字节,便会损失精度。
JS 中整数的最大安全范围可以查到是:9007199254740991
为了避免出现精度损失,我们可以采用另一种方法来巧妙地运算
使用字符串进行计算
将每一个字符当做一位来单独进行计算,将其分出来非进位和与进位和,将两者进行一个字符串的拼接
let a = "9007199254740991";
let b = "1234567899999999999";
function add(a ,b){
//取两个数字的最大长度
let maxLength = Math.max(a.length, b.length);
//用0去补齐长度
a = a.padStart(maxLength , 0);//"0009007199254740991"
b = b.padStart(maxLength , 0);//"1234567899999999999"
//定义加法过程中需要用到的变量
let t = 0;
let f = 0; //"进位"
let sum = "";
for(let i=maxLength-1 ; i>=0 ; i--){
t = parseInt(a[i]) + parseInt(b[i]) + f; //每一位字符的number和
f = Math.floor(t/10); //将和进行一个进位(类似<<)处理,并取出”进位“,这个进位要在下一位运算中加上
sum = t%10 + sum; //将第i位的字符结果拼接到sum上
}
if(f == 1){
sum = "1" + sum;
}
return sum;
}
4.6,JS该不该用位运算?
JS中最大数支持64bit,但是注意运算起来,如果是两个数,比如位操作,最大支持31bit(2147483647),如果超出之后,比如
2147483647 & 2147483647 结果与之后是32位,这个不支持已经溢出,所以安全的位操作的最大位数30位
2147483647 & 2147483647 // 2147483647
4294967295 & 4294967295 // -1
没有什么必要在 js 中使用位运算,未必就能提高性能,引擎未必不会帮我们优化,由于 number 类型存储的方式,就可能会踩坑。
小结
这套运算符针对的是整数,所以对 Javascript 完全无用,因为 Javascript 内部,所有数字都保存为双精度浮点数。如果使用它们的话,Javascript 不得不将运算数先转为整数,然后再进行运算,这样就降低了速度。而且"按位与运算符"&同"逻辑与运算符"&&,很容易混淆。
5. 写在最后:
以上的例子在平常可能会比较容易用到或看到,也是属于比较容易理解的。一些比较复杂的、难理解的,对于我来说,觉得应该尽量少用,因为会给自己带来麻烦。但是巧妙的使用位运算可以大量减少运行开销,优化算法。我们都知道计算机存储的都是二进制数据,这是由计算机本身的设计决定的。
参考资料:
https://www.w3school.com.cn/js/js_bitwise.asp
https://blog.csdn.net/deaidai/article/details/78167367
https://blog.csdn.net/weixin_43808903/article/details/ 110262130