如何快速判断一个数是否为2的倍数?(正整数)

如何快速判断一个数是否为2的倍数?(正整数)

解法一:暴力解法

 一个数是否为2的倍数,可分为两者情况1和非1。

 1显然是2的倍数,至于其他情况我们仅需暴力模2、处2循环,一旦出现模2结果非0的情况,则表明该数不是2的倍数!

bool func(int x) // x >= 0
{
   
   
	if (x == 0) return true;
	else
	{
   
   
		while (x)
		{
   
   
			if (x % 2) return false;
			x /= 2;
		}
		return true;
	}
}

解法二:lowbit(x&-x)

 首先,如果一个数是2的倍数,此时该数都对应的二进制只存在唯一 一个1(比如8对应1000

<think> 我们想要将一个转化为8的倍数(即向上或向下取整到最近的8的倍数)。通常有两种需求: 1. 向下取整(floor):找到不大于该的最大8的倍数2. 向上取整(ceil):找到不小于该的最小8的倍数。 注意:8是2的3次方,因此可以利用位运算来高效实现。 方法: 1. 向下取整:去掉最低的几位二进制位(具体是去掉最后3位,因为8=2^3)。 公式:$$x \& \sim7$$ 或 $$x - (x \& 7)$$ 或 $$(x >> 3) << 3$$ 解释:由于8的倍数要求最低3位为0,所以将原与上~7(即0xFFFFFFF8,对于32位整)就可以将最后3位置0。 2. 向上取整:如果x已经是8的倍数,则直接返回;否则,先向下取整再加8。 公式:$$(x + 7) \& \sim7$$ 或 $$((x + 7) >> 3) << 3$$ 或 $$(x | 7) + 1$$(但注意如果x最后三位全1,则x|7还是x,然后+1就会进位,所以正确) 但是,更常见的做法是:$$((x - 1) | 7) + 1$$ 并不如加7然后与~7来得直接。 推荐使用: 向下取整: x & ~7 向上取整: (x + 7) & ~7 注意:以上方法适用于无符号整,对于有符号整,在正范围内同样适用,负则需要注意(因为负的取整方向与正不同,比如向下取整对于负来说是更小的)。如果考虑负情况,需要明确需求: 通常,对于内存对齐,我们使用向上取整(比如分配内存)时,负是不会出现的。所以一般假设非负整。 但如果需要处理负,那么: - 向下取整(向负无穷方向):仍然可以使用 x & ~7,因为位运算对于负的处理是符合二进制位的截断的(但注意,在C语言中,对于负的右移是 implementation-defined,通常算术右移会保留符号位,因此使用位与操作更安全)。 - 向上取整(向正无穷方向):负情况下,向上取整实际上是取绝对值更小的(即更大的)。我们可以用类似的方法:对于负,我们可以先调整再计算。不过,如果负采用补码表示,且我们使用上面的公式,那么: (x + 7) & ~7 对于负也是有效的吗? 让我们验证负情况(假设32位整): 假设x = -9,我们期望向上取整到8的倍数:应该是-8(因为-8比-16更接近0,且大于-9)。 -9的二进制(补码):1111 1111 1111 1111 1111 1111 1111 0111 -9 + 7 = -2: 二进制为 1111 ... 1110 (30个1然后10) 然后我们与~7(即0xFFFFFFF8)相与:即最后3位置0,得到1111 ... 1110 000 (最后三位000),这就是-8的补码表示。 所以,公式 (x+7) & ~7 对于负也适用。 但是,向下取整对于负: x = -9,向下取整应该是-16(因为-16比-8小,且是8的倍数),但我们用 x & ~7 得到: -9 & ~7 = 1111...11110111 & 1111...11111000 = 1111...11110000,即-16。 这符合向下取整(向负无穷方向)的要求。 然而,有时候“向下取整”可能被理解为向0取整,那么对于负,我们希望取到比它大的那个8的倍数(即向0靠近)。例如,-9向0取整应该是-8(因为-8比-9大且更靠近0)。这时,我们就不能简单地用x & ~7。 因此,我们需要明确需求: - 内存对齐通常使用向下对齐(即返回的地址不大于原地址)?还是向上对齐(分配足够空间)? 在内存分配时,为了包含任意位置开始的一段内存,我们通常需要向上取整(以确保足够的空间)。例如,malloc分配内存时,为了对齐,实际分配的内存地址是向上取整的。 所以,在内存对齐的上下文中,我们通常需要: 分配内存大小:向上取整到8的倍数(例如,请求分配10字节,实际分配16字节) 地址对齐:给定一个地址,要找到下一个对齐的地址(向上取整),但注意这里地址一般是无符号的。 因此,针对非负整(内存大小或地址)的通用方法: 向下取整到8的倍数: #define ALIGN_DOWN(x) ((x) & ~7) 向上取整到8的倍数: #define ALIGN_UP(x) (((x)+7) & ~7) 如果x可能是负,并且我们想要向0取整,那么需要判断: 如果x>=0,则向下取整(即ALIGN_DOWN(x));如果x<0,则向上取整(即ALIGN_UP(x))?但注意,向0取整对于负就是取比它大的那个倍数(即ALIGN_UP(x)),而对于正就是取比它小的那个倍数(即ALIGN_DOWN(x))。所以: #define ALIGN_TO_ZERO(x) ( (x)>=0 ? ALIGN_DOWN(x) : ALIGN_UP(x) ) 不过,在内存对齐中,负无意义,因此我们通常只考虑非负。 代码示例(针对非负整): */ </think> 在C语言中,将任意整快速对齐到8的倍数(即地址或大小满足 $address \% 8 = 0$)可通过位运算高效实现,以下是两种核心方法: ### 方法1:向下对齐(取不大于x的最大8倍数) ```c #define ALIGN_DOWN(x) ((x) & ~7) // 清零最后3位二进制 ``` **原理** 8的倍数二进制特性:**最低3位全为0** $$x \text{ \& } \sim7 = x \text{ \& } 0xFFFFFFF8$$ 示例: ```c int x = 22; // 二进制: 00010110 int aligned = x & ~7; // 结果: 00010000 (16) ``` ### 方法2:向上对齐(取不小于x的最小8倍数) ```c #define ALIGN_UP(x) (((x) + 7) & ~7) ``` **原理** 先补偿7确保跨越对齐边界,再清零后3位: $$\text{align\_up} = \begin{cases} x & \text{if } x \% 8 = 0 \\ x + (8 - x\%8) & \text{otherwise} \end{cases}$$ 示例: ```c int x = 17; // 17 % 8 = 1 int aligned = (17+7) & ~7; // 24: 00011000 ``` --- ### 验证与应用场景 ```c #include <stdio.h> #include <assert.h> #define ALIGN_DOWN(x) ((x) & ~7) #define ALIGN_UP(x) (((x) + 7) & ~7) int main() { // 验证对齐 assert(ALIGN_DOWN(30) == 24); // 24 ≤ 30 assert(ALIGN_UP(15) == 16); // 16 ≥ 15 // 内存分配对齐(如自定义内存池) void* ptr = malloc(100); void* aligned_ptr = (void*)ALIGN_UP((uintptr_t)ptr); printf("原始地址: %p\n", ptr); printf("对齐地址: %p\n", aligned_ptr); return 0; } ``` ### 关键点说明 1. **位运算优势** - 避免除法/取模运算($\%$ 和 $/$ 在硬件层较慢) - 单周期完成:AND和ADD是CPU基础指令 2. **负处理** 上述宏适用于有符号整(补码机制下位运算仍有效): ```c ALIGN_DOWN(-10); // -16 (0xFFFFFFF0) ALIGN_UP(-5); // 0 (0x00000000) ``` 3. **边界验证** 当 $x$ 已是8倍数时: ```c ALIGN_DOWN(16); // 16 ALIGN_UP(24); // 24 ``` --- ### 性能对比 | 方法 | 指令周期(x86) | 适用场景 | |------------------|----------------|------------------| | `x & ~7` | 1 cycle | 地址向下对齐 | | `(x+7) & ~7` | 2 cycles | 内存大小向上对齐 | | `((x+7)/8)*8` | 10+ cycles | 不推荐 | > 在Linux内核(如`include/linux/align.h`)和malloc实现中广泛使用位运算对齐[^1][^3]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白debug~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值