为何Arduino此位移位操作可行?将字节拆分为两个4位有符号数
为什么左移12位能正确提取4位有符号数?
先明确前提:你的Arduino Leonardo是AVR架构,编译器里的int是16位有符号整数,C语言在对小于int的类型(比如int8_t)做位运算时,会先把操作数提升为int类型,这是问题的核心。
先看正确的高4位提取(dx=-2)
int8_t dx = (b >> 4);
b是int8_t,值为0b11101111(十进制-17),提升为16位int后变成0xFFEF(符号扩展:8位的最高位1,扩展到16位的高8位)。- 算术右移4位时,因为
int是有符号类型,会补符号位(1),得到0xFFFE(十进制-2),转成int8_t就是-2——这正好是高4位0b1110作为4位有符号数的正确值(4位补码1110对应-2)。
为什么左移4位再右移不行?
如果尝试(b <<4) >>4,过程是这样的:
b提升为int是0xFFEF,左移4位得到0xFEF0(十进制-272),此时原来的高4位0b1110还留在16位int的高8位区域。- 算术右移4位时,符号位是
0xFEF0的最高位1,右移会补1,最终得到0xFFF0(十进制-16),转成int8_t是0xF0(-16),完全不是我们要的低4位值。
左移12位生效的原因
int8_t dy = (b << 12) >> 12;
b提升为int是0xFFEF,左移12位时:原来的低4位0b1111会被移到16位int的最高4位(bit12~bit15),而原来的高4位和其他高位会被溢出丢弃,结果是0xF000(二进制1111000000000000)。- 算术右移12位时,
0xF000的最高位是1,右移会补1,最终得到0xFFFF(十进制-1),转成int8_t就是0xFF(-1)——这正是低4位0b1111作为4位有符号数的正确值(4位补码1111对应-1)。
更直观的替代写法
如果觉得左移12位的写法不够直观,可以用更清晰的方式提取低4位并做符号扩展:
// 提取低4位后,根据最高位是否为1,手动补高4位的符号位 int8_t dy = (int8_t)((b & 0x0F) | ((b & 0x08) ? 0xF0 : 0x00)); // 或者用类型转换明确控制符号扩展 int8_t dy = (int8_t)((int16_t)(b & 0x0F) << 12) >> 12;
内容的提问来源于stack exchange,提问作者ohobbs72
相关产品推荐
相关产品推荐

