为何两段提取4位二进制位的代码表现不同?(正负整数转十六进制)
正负整数转十六进制:两种4位提取写法的差异原因
我们需要实现正负整数转十六进制的功能,核心逻辑是从整数中按4位一组提取二进制位并转换为十六进制字符。以下是两种提取4位二进制位的写法,写法2能正常处理正负整数,但写法1结果异常,具体原因如下:
原代码及两种写法对比
完整转十六进制代码
string toHex(int num) { string ans = ""; // 按4位一组遍历整数 for (int i = 28; i >= 0; i -= 4) { // 存在问题的写法1 /***********************************************/ // 提取从第i位开始的4位二进制 int number_before = (num & (15 << i)); number_before = (number_before >> i) ; /***********************************************/ // 转换为十六进制字符并追加到结果 if(number_before == 0 && ans.size() == 0) continue; else if (number_before < 10) { ans += (char)(number_before + '0'); // 0-9转'0'-'9' } else { ans += (char)(number_before - 10 + 'A'); // 10-15转'A'-'F' } } return ans; }
两种提取写法
写法1(异常)
int number_before = (num & (15 << i)); number_before = (number_before >> i) ;
写法2(正常)
int number_before = ((num>> i) & 15 );
问题原因分析
核心问题出在有符号整数的算术右移特性和15 << i的溢出行为:
写法1的问题拆解
- 第一步
num & (15 << i):当i=28时,15(二进制0xF)左移28位后,对于32位有符号int来说,结果的最高位(符号位)会被置1,变成一个负数(比如0xF0000000,对应十进制-16777216)。如果原num是负数,按位与后得到的number_before也是一个高位为1的负数。 - 第二步
number_before >> i:C++中,有符号整数的右移是算术右移——负数右移时会在高位补1而不是0。比如0xF0000000右移28位后,会变成0xFFFFFFFF(即十进制-1),而不是我们期望的0xF,这直接导致后续转换的十六进制字符错误。
- 第一步
写法2的正确性逻辑
- 先执行
num >> i:负数做算术右移时会补符号位,但紧接着我们和15(二进制0b1111)做按位与操作。15的高位全为0,会把右移后补的所有1全部清零,只保留低4位的有效数值,最终得到的就是我们需要的0-15范围内的正确值,后续转换自然正常。
- 先执行
内容的提问来源于stack exchange,提问作者Satyam Mittal
相关产品推荐
相关产品推荐

