图片来源:https://www.pixiv.net/artworks/81470216
预备知识
算术右移
算术右移会保留符号位(或者填充符号位),这样可以保证左移一位等于除以2 ,算术上符合直觉;但如果只是观察bit位变化,对于负数来说,算术右移看起来就是不断在左边填充“1”,和其他位移操作相比,显得不那么符合直觉(无符号数左移/右移、正数算术左移/右移、负数算术左移都可以理解为填充“0”)。
C++ 20 明确规定了对于有符号数的右移为算术右移,C++ 20 前虽没明确规定,但大多编译器的实现上也是算术右移:
For negative a, the value of a >> b is implementation-defined (in most implementations, this performs arithmetic right shift, so that the result remains negative). (until C++20)
The value of a >> b is a/2b, rounded down (in other words, right shift on signed a is arithmetic right shift). (since C++20)
Arithmetic operators
隐式类型转换
C++ 算术运算符不接受小于“int
”的类型,所以位移运算前会把 signed char
转换成 int
,unsigned char
转换成 unsigned int
。观察bit位变化,对于无符号数和有符号正数来说,转换后额外bit位填充的是“0” ,对于负数来说转换后额外bit位填充的是“1”。
循环位移模板实现(C++ 20)
#ifndef ROTATE_NBIT_
#define ROTATE_NBIT_
#include <concepts>
#include <cstddef>
template <std::integral interger_t>
interger_t RotateLeftNbits(interger_t value, size_t n) {
// 将有符号数转换为对应类型的无符号数
std::make_unsigned_t<interger_t> uvalue = value;
// 计算bit总长度
size_t bit_num = sizeof(uvalue) * 8;
// 避免多余的位移
n %= bit_num;
return interger_t(uvalue << n | uvalue >> (bit_num - n));
}
template <std::integral interger_t>
interger_t RotateRightNbits(interger_t value, size_t n) {
std::make_unsigned_t<interger_t> uvalue = value;
size_t bit_num = sizeof(uvalue) * 8;
n %= bit_num;
return interger_t(uvalue >> n | uvalue << (bit_num - n));
}
#endif /* ROTATE_NBIT_ */
参考:
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。