技巧:求能被size整除的大于或等于num的最小值

最近看到CSDN上的一篇博文,提到了从驱动代码里发现的一个奇怪宏,如下:

#define E1000_ROUNDUP(num, size) ((num) = (((num) + (size) - 1) & ~((size) - 1)))

那篇文章的作者通过执行代码认为此代码的作用是求能被size整除的大于或等于num的最小值(size要为偶数),并且没有给出解释。

实际上,这段代码的作用确实是求能被size整除的大于或等于num的最小值。但size的条件要示比较高,要为2的幂才可以。
我们可以试想这个宏是怎么想出来的。设size是2的n次幂,num共有m+n个二进制位。要求能被size整除的大于或等于num的最小值,可以分为2部分来考虑:整除和进位。

首先是进位。如果num没有恰好被size整除的话,肯定要在后n位加上待定的值来使前m位进1。暂且不考虑后n位,只要让前m位加1,即加上2的n次方即可达到目的。但是如果num正好能被size整除,就不能进位了。因此可以加上2的n次方减1(即size-1)。

再次是整除,要被size整除,也就是要把num的后n位清零,同时保证前m位不变(因为进位的要求已经达到了)。C语言位操作中的“与”操作常用来完成 位清零和位保持的工作。只要将num与后n位为0的数作与操作即可将后n位清0。其余的二进制位与1作与操作即可保持相应位的值不变。因此,我们只要构造 一个后n 位为0,其余各位为1的数即可。由于1的位数要根据num值的大小来确定,因此这个数不好构造。但后n位为1,其余各位为0的数很好构造,就是size- 1。只要将size-1的各位按位取反就可得到我们想要的数。因此整除的操作可以通过“&~(size-1)”来完成。

综合起来,就是num=(num+size-1)&~(size-1)。将它变成宏的时候要注意将各参数都用括号括起来,防止宏展开的时候出现错误。因此就有了开篇的那句代码:

#define E1000_ROUNDUP(num, size) ((num) = (((num) + (size) - 1) & ~((size) - 1)))

从这个宏我们可以看出位操作的强大功能。与2的幂相关很多问题都可以用位参数来实现。如果不用位操作,本题可以用用如下代码来实现:

num=(num%size==0)?num:(num+size-num%size);

或者

num=(num%size==0)?num:(num/size+1)*size;

很多时候我们都要求能被size整除的大于或等于num的最小值。比如Linux下的DES加密函数,要求只能对8字节的整数倍的数据进行加密,如果不 是,就要在数据的后面补零变成8字节的倍数再进行加密。8正好是2的幂,补0后的字节数就可以用上面的宏来求。同样,将一段数据写到操作系统的分页 (4096字节)里也可以用上述的宏。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值