原创文章,转载请注明出处,谢谢!
作者:清林,博客名:飞空静渡
对于编程人员来说,和2的n次方打交道那是经常的事了。
在这里,我想先说一下2的n次方的一些特点,2的n次方是什么样的数呢,像1,2,4,8,16,32 ...... 512,1024 ......等等之类的数就是
2的n次方的数。
2的n次方的值从另一方面来说也就是1向左移动一些位数的值,如:1是2^0 == 1<<0,2是2^1 == 1<<1, 8是2^3 == 1<<3等等。
在这里,我想说两个关于2的n次方的两个点,这也是在看linux代码时经常出现的。
第一个:
在这里我先问一下:假设给出一个变量val,如何判断这个变量的值是2的n次方呢!--------先想一想。
答案是:
if (val && (val -1)) { cout<<"not a 2^n variable"<<endl; return -1; }
这里利用了2的n次方的另一个特点,我上面说道,2的n次方是1<<n的值,这里即是说2的n次方的数的最高位为1,其余位都为0的数。
下面看下:
1,2,4,8,16 ......
的二进制数为:
1, 10, 100, 1000, 10000 ......
所以val是2^n的数的话,那么val-1的值就是对val取反了,即是高位为0,其余地位都为1,所以如果val为2^n,那么val & (val-1)的值就一定为0。
这个判断表达式在判断一些变量值的合法性是经常使用的,比如,判断磁盘扇区的大小和分区簇的大小的合法性时就会经常用到,因此扇区的大小和簇的大小一定是要2^n的值。
第二个问题是相除:
我们都知道,计算机对数值的计算最擅长的就是移位。
如果我们要做的操作是乘于一个数,或除于一个数,并且这个被乘数或被除数是2^n方的话,那么我们最好的操作就是移位,因为直接乘于一个数或除于一个数还不如移位的计算速度来得快。下面举一些例子来说明。
假如下面有几个变量:
unsigned long long number_of_sector; // 表示这个分区的扇区总数,即是这个分区的大小
unsigned long long nr_cluster; // 表示这个分区的簇总数,也是这个分区的大小
int sector_per_cluster; // 表示每簇有多少个扇区,即是簇的大小
int sector_size; // 扇区大小
如果我们知道number_of_sector 和 sector_per_cluster的大小,那么我们就可以计算到nr_cluster的值。
nr_cluster = number_of_sector / sector_per_cluster;
我们这样计算是没有什么问题的,但是如果我们想要优化一下,我们就得看下下一种表示了。
首先,sector_per_cluster是一个2^n的数,那么除于这个数就可以表示成向右移动一定的位数,即
nr_cluster = number_of_sector / sector_per_cluster
== nr_cluster = number_of_sector * (1>> n)
== nr_cluster = number_of_sector >> n
那么这个n是多少呢,例如如果sector_per_cluster是2的话,那么这个n就是1,如果sector_per_cluster是4的话,那么这个n就是2,如果sector_per_cluster是8的话,那么这个n就是3 。。。。。。
看出规律了没有,就是sector_per_cluster这个2^n的n的值,其实也是1<<n的这个n的值,最后这个n就是这个数值的最高1位的位置值减1,很坳口是吧,举例来说,2的二进数是10,这个1的位置是2,n=2-1=1。再举例来说8,其二进制数是1000,其高位1的位置是4,那么n=4-1=3。明白了吧!那么我们假设这个最高位1的数字是w,那么我们前面的表达式就可以写成这样:
nr_cluster = number_of_sector >>(w-1);
最后的问题就剩下了这个w怎么计算了,别怕,看下linux的下的代码,我把它拿出来给你看:
/** * ffs - Find the first set bit in an int * @x: * * Description... * * Returns: */ int ffs(int x) { int r = 1; if (!x) return 0; if (!(x & 0xffff)) { x >>= 16; r += 16; } if (!(x & 0xff)) { x >>= 8; r += 8; } if (!(x & 0xf)) { x >>= 4; r += 4; } if (!(x & 3)) { x >>= 2; r += 2; } if (!(x & 1)) { x >>= 1; r += 1; } return r; }
这个int ffs(int x)的函数就是计算x的最高位1的位置值的,慢慢体会吧 : )
那么我们前面的表达式就可以写成这样了:
nr_cluster = number_of_sector / sector_per_cluster;
==nr_cluster = number_of_sector >>(ffs(sector_per_cluster) - 1);
如果我们有看一些源代码的话,我们也会发现,有很多的变量会有相应的表示其需要移位的位数的一个变量。例如:
sector_per_cluster就会有sector_per_cluster_bits
sector_size就会有sector_size_bits
sector_per_cluster_bits = ffs(sector_per_cluster) -1;
sector_size = ffs(sector_size) -1;
为什么需要这样的变量呢,其实也是为了优化和方便。例如有个变量 cluster_size表示簇的大小,那么它的值就可以这样计算:
cluster_size = sector_size * sector_per_cluster;
但是如果我们有sector_per_cluster_bits这样的变量,上面的表达式就可以这样计算了:
cluster_size = sector_size <<sector_per_cluster_bits;
所以说,如果我们做一个乘于或除于一个数时,如果这个数是2^n的话,那么我们就应该考虑向左或向右移动一定的位数来代替乘于或除于的计算了。