判断一个正整数是否是2的N次方的简洁算法及其证明
在写代码时遇到了“判断一个正整数是否是2的N次方”的问题,不想调用 java.lang 的 Math 类库进行浮点运算,觉得转换为浮点不是个好办法。
遂在网上搜索了一下,发现有人列出来好几种写法,列举几种:
1、通过循环除2;这种方法不值一提,略过;
2、针对32位/64位只有有限个 2 的N次方的常量值,逐个进行比较;额。。。这个也略过;
3、通过正则表达式进行文本匹配,判断是否2的后面都是 0 ;这个绕得更远了。。。
最后,有一种最简洁优雅的写法:(value & (value -1)) == 0;
喔,的确是简洁优雅!!!
不过,等等,接下来有人提出,似乎“所有2的N次方的结果都符合这个表达式”这点很容易证明;
可是如何证明符合条件“(value & (value -1)) == 0”的一定就是 2 的 N 次方呢?(N 是整数且大于等于0)。
想了一下,证明也不难,遂在此记下:
1、首先,记 A = value; B = value - 1;
2、既然 A & B == 0,那么意味着,A和B的二进制形式中,每一位都不相同;(例外的情况只有“两者都是 0” ,否则存在相同位的两个数的按位相与的结果不可能为 0)
3、由于 B = A - 1,即 A > B;基于第2点,A 和 B 每一位都不同,则可以推断出只有两种情况:
(1)以二进制形式, A 最高位 1 与 B 的最高位 1 的位数相差 1 ;(x表示后面跟随的位数是 0 位到多位)
A: 10xxxxxxx
B: 01xxxxxxx
(2)第二种情况就是:A = 1,B = 0;
显然第二种情况是符合命题的,因为 N = 0 ,2 的 N 次方的结果为 1 ; 接下来继续针对第一种情况做推导。
4、由于 A 与 B 仅相差1,那意味着在 B 的二进制的末尾加上 1 ,将会连续地向高位产生进位,最终导致 B 的最高位 01 进位为 10 ;
注意,二进制形式中,能够“连续向高位产生进位”的情况只有一种,即 xxxxxxx 全部都是 1 ,也就是说 B 的全部是 1 ;
由此,基于第2点,A 和 B 的每一位都不同,那么 A 除了最高位 1 之外,所有低位都是 0 ;
由此证得命题!