feat(lang/optimizations): 添加有符号移位相关说明 (#4711)

* feat(lang/optimizations): 添加有符号移位相关说明

* style: format markdown files with remark-lint

---------

Co-authored-by: 24OI-bot <15963390+24OI-bot@users.noreply.github.com>
pull/4717/head
Yingchi Long 2023-02-12 22:57:10 +08:00 committed by GitHub
parent df71386a5e
commit c179934094
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 1 deletions

View File

@ -48,7 +48,7 @@ int binary_search(int start, int end, int key) {
```
???+note
对于 $n$ 是有符号数的情况,当你可以保证 $n\ge 0$ 时,`n >> 1` 比 `n / 2` 指令数更少。
参考 [编译优化 #位运算代替乘法](/lang/optimizations/#%E4%BD%8D%E8%BF%90%E7%AE%97%E4%BB%A3%E6%9B%BF%E4%B9%98%E6%B3%95)对于 $n$ 是有符号数的情况,当你可以保证 $n\ge 0$ 时,`n >> 1` 比 `n / 2` 指令数更少。
### 最大值最小化

View File

@ -409,6 +409,32 @@ a = x * 2; // bad!
a = x << 1; // good!
```
需要注意的是有符号数和无符号数在移位 (shifting) 和类型提升 (promotion) 层面有明显的差异。符号位在移位时有着特别的处理,包括算术移位和逻辑移位两种类型。这在编写二分查找/线段树等含有大量除二操作的时候表现突出,有符号整数除法不能直接优化为一步右移位运算。
```c++
int l, r;
/* codes */
int mid = (l + r) / 2; /* 如果编译器不能假定 l, r 非负,则会生成较差的代码 */
// 不能优化为
// mid = (l + r) >> 1
// 反例:
// mid = -127
// mid / 2 = -63
// mid >> 1 = -64
```
```c++
int mid = (l + r);
int sign = mid >> 31; /* 逻辑右移, 得到符号位 */
mid += sign;
mid >>= 1; /* 算术右移 */
```
可行的解决方案:
- 用 `unsigned l, r;`,下标本来就应该是无符号的
- 在源代码中使用移位
##### 乘法代替除法
```cpp