一个unsigned int 数的二进制表示中有多少个1

本文介绍了几种计算整数中1的位数的方法,包括通过循环逐位检查、利用位运算特性减少循环次数以及使用复杂的位操作快速计算。
这是一道面试题可以用以下的一些方案。
第一种是很容易想到的采用循环的方式并且与1进行位与运算,具体代码如下。
 1 求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? unsigned  int  GetBitNumOfOne_ByLoop1(unsigned  int  nValue)
 2 求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? {
 3求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? const unsigned int nNumOfBitInByte = 8;
 4求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? unsigned int nBitMask = 1;
 5求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? unsigned int nBitNum = 0;
 6求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? for(unsigned int = 0 < sizeof(nValue) * nNumOfBitInByte i++)
 7求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? {
 8求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?  (0 < (nValue & nBitMask)) ? nBitNum++ 0;
 9求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?  nBitMask<<=1;
10求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? }
11求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? return nBitNum;
12求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?}
13 求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?unsigned  int  GetBitNumOfOne_ByLoop2(unsigned  int  nValue)
14 求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? {
15求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? const unsigned int nNumOfBitInByte = 8;
16求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? unsigned int nBitMask = 1;
17求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? unsigned int nBitNum = 0;
18求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? for(unsigned int = 0 < sizeof(nValue) * nNumOfBitInByte i++)
19求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? {
20求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?  (0 < (nValue & nBitMask)) ? nBitNum++ 0;
21求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?  nValue>>=1;
22求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? }
23求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? return nBitNum;
24求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?}

这两种做法很相像,区别就是在对nBitMask进行左移还是对nValue进行右移。
当然了以上的两个方法存在一个问题:不管如何这个函数肯定要循环32次(对于32平台来说)。
那又没有更好的方法?当然有,请看下面:
 1 求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? unsigned  int  GetBitNumOfOne_ByLoop3(unsigned  int  nValue)
 2 求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? {
 3求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? unsigned int nBitNum = 0;
 4求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? while(0 < nValue)
 5求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? {
 6求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?  nValue &=(nValue - 1);
 7求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?  nBitNum++;
 8求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? }
 9求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? return nBitNum;
10求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?}

假如使用参数12345(二进制是11000000111001)调用该函数,该函数的执行情况如下:
第一次进入循环
0 < 11000000111001
11000000111001 &= (11000000111001 - 1) 之后 nValue 的值是 11000000111000
nBitNum 的值是1
经过本次循环之后11000000111001 变成了 11000000111000 比之前少了一个1
第二次进入循环
0 < 11000000111000
11000000111000 &= (11000000111000 - 1) 之后 nValue 的值是 11000000110000
nBitNum 的值是2
经过本次循环之后11000000111000 变成了 11000000110000 比之前少了一个1
第三次进入循环
0 < 11000000110000
11000000110000 &= (11000000110000 - 1) 之后 nValue 的值是 11000000100000
nBitNum 的值是3
经过本次循环之后11000000110000 变成了 11000000100000 比之前少了一个1
经过以上3次循环情况的说明,我相信你一定看出了些什么吧。nValue &=(nValue -1),这句
代码实际上就是把nValue 的某位及其以后的所有位都变成0,当nValue最后变成0的时候循环结束,
且nBitNum 记录的就是1的个数。
上面的做法已经很不错了,但是作为程序员的你是否会有疑问,“还有其他的方法吗?”。
有,当然有!请看下面的代码:
 1 求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? unsigned  int  GetBitNumOfOne(unsigned  int  nValue)
 2 求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? {
 3求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? nValue = ((0xaaaaaaaa & nValue)>>1+ (0x55555555 & nValue);
 4求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? nValue = ((0xcccccccc & nValue)>>2+ (0x33333333 & nValue);
 5求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? nValue = ((0xf0f0f0f0 & nValue)>>4+ (0x0f0f0f0f & nValue);
 6求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? nValue = ((0xff00ff00 & nValue)>>8+ (0x00ff00ff & nValue);
 7求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? nValue = ((0xffff0000 & nValue)>>16+ (0x0000ffff & nValue);
 8求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? 
 9求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1? return nValue;
10求一个unsigned <wbr>int <wbr>数的二进制表示中有多少个1?}


假如你是第一次看到这些代码,你是否能看明白?呵呵,本人第一次看到这些代码的时候看了好久才感觉
好像有点明白。下面我就以一个例子来说明上面的代码是如何做到的。
假如参数是0xffffffff。
第一行代码:
nValue = ((0xaaaaaaaa &nValue)>>1) + (0x55555555& nValue);
a的二进制表示是:1010
5的二进制表示是:0101
0xffffffff 与 0xaaaaaaaa进行与运算之后是0x10101010101010101010101010101010
然后再进行左移操作后是0x01010101010101010101010101010101
0x55555555 & nValue进行与运算之后是0x01010101010101010101010101010101
然后0x01010101010101010101010101010101 &0x01010101010101010101010101010101
得到0x10101010101010101010101010101010
我们把得到的结果分成16组0x10  10 10  10  10 10  10  10 10  10  10 10  10  10 10  10
每组的10单独来看是不是十进制的2
我们0x01010101010101010101010101010101和0x01010101010101010101010101010101这两个数也按照上面的方法分成
16个组0x01  01 01  01  01 01  01  01 01  01  01 01  01  01 01  01
     0x01  01  01 01  01  01 01  01  01 01  01  01 01  01  01 01
那么这两个数的每一个组都是01 那么两个01里面有几个1,是不是2个。这是2是不是二进制的10,然后16个10组合起来是不是
0x10101010101010101010101010101010

第二行代码:
nValue = ((0xcccccccc &nValue)>>2) + (0x33333333& nValue);
此时nValue是0x10101010101010101010101010101010
c的二进制表示是:1100
3的二进制表示是:0011
0xcccccccc 与 nValue)进行与运算之后是0x10001000100010001000100010001000
然后再进行左移操作后是0x00100010001000100010001000100010
0x33333333 & nValue进行与运算之后是0x00100010001000100010001000100010

然后0x00100010001000100010001000100010 &0x00100010001000100010001000100010
得到0x01000100010001000100010001000100
我们把得到的结果分成8组0x0100 0100 0100 0100 0100 0100 0100 0100
每组的0100单独来看是不是十进制的4 总共有多少个4?是不是8个,8×4=32。

以下的代码:
 nValue = ((0xf0f0f0f0 &nValue)>>4) + (0x0f0f0f0f& nValue);
 nValue = ((0xff00ff00 &nValue)>>8) + (0x00ff00ff& nValue);
 nValue = ((0xffff0000 &nValue)>>16) + (0x0000ffff& nValue);
请自己按照上面的方法做一遍,就会发现规律:第一次32位数分成32组,第二次分成16组,第三次分成8,第四次分成4,第五次分成2组。
如果还是不明白请多看看,然后多选择几个参数进行试验,多试几次肯定会明白的。

<think>我们被要求判断unsigned long int二进制。在C/C++中,不同平台和编译器下,unsigned long int的位可能不同(常见的是32位或64位)。我们可以使用两种方法: 1. 使用sizeof运算符计算字节,然后乘以8得到位2. 通过计算该类型能表示的最大无符号整二进制(即计算最大值有多少位)。 注意:由于引用中提到了不同整类型的位(引用[3]),但具体到unsigned long int,引用[3]中给出的是32位(在它那个例子中),但实际中可能是不同的。因此,我们需要一个通用方法。 方法一:使用sizeof 直接使用sizeof(unsigned long int) * 8,因为一个字节等于8位。 方法二:通过最大值 我们可以利用<climits>(或<limits.h>)中的宏ULONG_MAX,然后计算这个值的二进制表示多少位。对于一个无符号整,其最大值的二进制就是该类型的位。计算方式:从1开始左移,直到移位后的值大于ULONG_MAX,移位的次就是位。或者用对:floor(log2(ULONG_MAX)) + 1。 但是,由于我们可能无法在编译前确定位(因为要在代码中计算),所以通常使用sizeof是更直接的方法。 然而,用户的问题是如何“查找”或“判断”,这可以是在编程时静态判断,也可以是在运行时计算。我们给出两种方式。 另外,引用[3]中提到了unsigned long int在C语言中的典型位(32位),但这不是绝对的。 因此,我们可以在程序中这样获取: 示例代码: #include <iostream> #include <climits> using namespace std; int main() { // 方法1:使用sizeof size_t sizeInBits = sizeof(unsigned long int) * 8; cout << "使用sizeof: " << sizeInBits << " bits" << endl; // 方法2:通过ULONG_MAX unsigned long int max_val = ULONG_MAX; size_t bit_count = 0; unsigned long int temp = max_val; while (temp) { bit_count++; temp >>= 1; } // 注意:如果max_val为0,则位为0,但unsigned long int不可能为0位,所以这里不考虑0的情况。 // 实际上,当max_val为0时,循环不会执行,所以bit_count为0,但这种情况不存在。 cout << "通过最大值计算: " << bit_count << " bits" << endl; return 0; } 但是注意:方法二在max_val为0时会出现问题(但实际上unsigned long int至少是16位,所以不会为0)。而且,方法二计算的是最大值的有效位,例如,如果最大值是0xFFFFFFFF(32位全1),那么循环会执行32次,得到32位。 然而,标准规定无符号整的最大值是$2^n -1$,其中n是位。因此,方法二计算出的位就是该类型的位。 但是,如果最大值不是全1(这种情况在标准整类型中不存在),所以方法二可靠。 另外,我们还可以使用内置的宏(如果存在),但标准没有提供直接获取位的宏。 在C++中,我们可以使用numeric_limits<unsigned long>::digits(在<limits>中),它直接给出位(不含符号位,对于无符号类型就是总位)。 所以更推荐的方法: #include <iostream> #include <limits> using namespace std; int main() { size_t bits = numeric_limits<unsigned long>::digits; cout << "unsigned long的位: " << bits << endl; return 0; } 这种方法更简洁,并且是编译时常量。 因此,总结: 在C++中,判断unsigned long int二进制,最可靠且简洁的方法是使用std::numeric_limits<unsigned long>::digits(需要包含<limits>头文件)。 注意:在C语言中,没有numeric_limits,所以可以使用sizeof方法或通过计算ULONG_MAX的位。 下面我们分别给出C++和C的示例: C++: #include <iostream> #include <limits> int main() { constexpr int bits = std::numeric_limits<unsigned long>::digits; std::cout << "C++: unsigned long is " << bits << " bits." << std::endl; return 0; } C语言: #include <stdio.h> #include <limits.h> int main() { // 方法1: sizeof size_t bits1 = sizeof(unsigned long) * CHAR_BIT; // CHAR_BIT在<limits.h>中定义,表示一个字节的位 // 方法2: 计算ULONG_MAX的位 unsigned long max = ULONG_MAX; size_t bits2 = 0; while (max) { bits2++; max >>= 1; } printf("C: using sizeof: %zu bits\n", bits1); printf("C: using max value: %zu bits\n", bits2); return 0; } 注意:在C语言中,使用sizeof方法更通用,因为循环方法在max为0时结果为0(虽然实际不会),而且当unsigned long很大时,循环次多。另外,使用CHAR_BIT是为了考虑不同系统中一个字节的位不一定为8(虽然现在绝大多系统都是8)。 因此,在C语言中,推荐使用sizeof结合CHAR_BIT的方法。 结论: 要判断unsigned long int二进制: - 在C++中,使用`std::numeric_limits<unsigned long>::digits` - 在C中,使用`sizeof(unsigned long) * CHAR_BIT` 引用说明: 引用[3]中提到了unsigned long int是32位,但这只是示例,具体取决于系统。 引用[2]和[3]都提到了unsigned关键字,但没有提供获取位的方法。 根据上述,我们给出答案。 注意:用户上一次的问题是关于生成unsigned long随机的,现在的问题是关于判断该类型的二进制,两者独立。 因此,我们直接回答即可。 </think>### 判断 `unsigned long int` 二进制的方法 在 C/C++ 中,`unsigned long int` 的位取决于编译器和平台(通常是 **32 位** 或 **64 位**)。以下是三种可靠的判断方法: --- #### 方法一:使用 `sizeof` 运算符(推荐) 通过计算类型占用的字节 × 每字节位: ```c #include <stdio.h> #include <limits.h> // 包含 CHAR_BIT int main() { size_t bits = sizeof(unsigned long int) * CHAR_BIT; // CHAR_BIT 表示每字节位 printf("unsigned long int二进制: %zu\n", bits); return 0; } ``` - **原理**:`sizeof` 返回类型占用的字节,`CHAR_BIT` 是系统常量(通常为 8)[^2][^3] - **优点**:跨平台兼容,编译时确定结果 - **输出示例**: - 32 位系统:`32` - 64 位系统:`64` --- #### 方法二:通过最大值移位计算 利用 `ULONG_MAX`(`unsigned long` 的最大值)计算有效位: ```c #include <stdio.h> #include <limits.h> int main() { unsigned long max_val = ULONG_MAX; size_t bits = 0; while (max_val) { bits++; max_val >>= 1; // 右移直到归零 } printf("unsigned long int 的有效位: %zu\n", bits); return 0; } ``` - **适用范围**:无符号整类型[^4] - **注意**:结果包含所有有效位(不包括前导零) --- #### 方法三:使用预定义宏(平台特定) 直接使用编译器定义的宏: ```c #include <stdio.h> int main() { #ifdef __LP64__ // 64 位系统宏 printf("64 位系统: 64 bits\n"); #else printf("32 位系统: 32 bits\n"); #endif return 0; } ``` - **常见宏**: - `__LP64__`:多 Unix-like 64 位系统 - `_WIN64`:Windows 64 位系统 - `__ILP32__`:32 位系统 - **局限性**:需预先知道平台宏定义 --- ### 关键概念说明 1. **类型位与平台关系**: - 32 位系统:通常 `unsigned long` 为 32 位(范围 $0$ 到 $2^{32}-1$) - 64 位 Unix 系统:通常为 64 位(范围 $0$ 到 $2^{64}-1$) - 64 位 Windows 系统:通常仍为 32 位[^3] 2. **二进制计算**: - 位 $n$ 与最大值关系:$\text{ULONG\_MAX} = 2^n - 1$ - 例:若 $\text{ULONG\_MAX} = 4294967295$ ($\approx 4.29 \times 10^9$),则 $n = 32$ 3. **无符号特性**: - 所有位均表示值(无符号位) - 最小值固定为 $0$[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值