前言
本文章主要介绍整数运算的内存表示,不是介绍运算的过程,具体运算的过程需要CPU逻辑处理单元的介入,这个后面的文章在介绍。本文主要分为下面几个方面:
无符号整数的加法
有符号整数的加法
无符号整数的乘法
有符号整数的乘法
乘以常数的优化
无符号整数的除法
有符号整数的除法
前置
- 无论是计算两个有符号整数之和还是计算两个无符号整数之和,对于大部分计算机来说,使用相同的指令,也就是说对于逻辑处理单元来说是一样的。由此我们能够推断出,如果计算两个有符号整数之和,我们可以按照如下的策略:
- 把补码按照原码进行解释,也就是有符号数变成了无符号数
- 然后把两个无符号数进行相加
- 把相加的结果按照补码解释,就得到正确的结果了
- 无论是计算两个有符号整数乘积还是计算两个无符号整数乘积,对于大部分计算机来说,使用相同的指令,也就是说对于逻辑处理单元来说是一样的。
- 在大多数机器上,整数乘法指令相当慢,需要10 个或者更多的时钟周期,然而其他整数运算(例如加法、减法、位级运算和移位) 只需要1个时钟周期。
无符号整数的加法
先看下面的例子:
unsigned short a = 40000;
unsigned short b = 40000;
unsigned short c = a + b;
printf("a = %hu, b = %hu, c = %hu\n",a,b,c);
运行结果如下:
a = 40000, b = 40000, c = 14464
为什么c=14464
呢?
↓
↓
↓
\downarrow\downarrow\downarrow
↓↓↓
对于无符号整数,都有一个范围,比如short类型占用两个字节,那么对于unsigned short
a
a
a,有
0000000000000000
≤
a
≤
1111111111111111
0000000000000000\le a \le1111111111111111
0000000000000000≤a≤1111111111111111
⇒
0
≤
a
≤
2
16
−
1
\Rightarrow 0\le a \le2^{16}-1
⇒0≤a≤216−1
如果还有另一个unsigned short
b
b
b,则
0
≤
c
1
=
a
+
b
≤
2
17
−
2
0\le c_1 = a+b \le2^{17}-2
0≤c1=a+b≤217−2
显然当
c
1
>
2
16
−
1
c_1>2^{16}-1
c1>216−1时,两个字节无法容纳
c
1
c_1
c1的值,这个时候就发生了溢出,计算机对这种溢出的处理非常简单,就是直接舍弃,或者截断。所以对这种情况,
c
c
c的值应该是:
c
=
c
1
−
2
16
c = c_1 - 2^{16}
c=c1−216。
我们对无符号整数的运算进行一下总结:
假设
a
a
a和
b
b
b都是占用
w
w
w位的无符号整数,则
a
+
b
a+b
a+b的运算结果如下:
c
=
{
a
+
b
,
a
+
b
<
2
w
a
+
b
−
2
w
,
2
w
≤
a
+
b
≤
2
w
+
1
−
2
c= \begin{cases} a + b,&a+b<2^w\\ a+b-2^w,&2^w\le a+b \le2^{w+1}-2 \end{cases}
c={a+b,a+b−2w,a+b<2w2w≤a+b≤2w+1−2
简单的一句话概括就是 c = ( a + b ) m o d 2 w c=(a+b)\mod {2^w} c=(a+b)mod2w
我们看最开始的例子:
a
=
40000
=
0
b
1001110001000000
a=40000=0b1001 1100 0100 0000
a=40000=0b1001110001000000
b
=
40000
=
0
b
1001110001000000
b=40000=0b1001 1100 0100 0000
b=40000=0b1001110001000000
a
+
b
=
40000
=
0
b
10011100010000000
a+b=40000=0b1 0011 1000 1000 0000
a+b=40000=0b10011100010000000
舍弃最高位后
c
=
0
b
0011100010000000
=
14464
c = 0b0011 1000 1000 0000 = 14 464
c=0b0011100010000000=14464
如何判断无符号整数相加发生了溢出
如果两个无符号整数相加 ( a + b > a ∣ ∣ a + b > b ) (a+b>a\ ||\ a+b>b) (a+b>a ∣∣ a+b>b),我们就说发生了溢出
bool isFull(unsigned short a,unsigned short b)
{
unsigned short c = a + b;
return (c>a) || (c>b);
}
有符号整数的加法
两个有符号整数相加情况复杂一些,看下面的例子:
signed short a = 20000;
signed short b = 20000;
signed short c = a + b;
printf("a = %hd, b = %hd, c = %hd\n",a,b,c);
signed short a1 = -20000;
signed short b1 = -20000;
signed short c1 = a1 + b1;
printf("a1 = %hd, b1 = %hd, c1 = %hd\n",a1,b1,c1);
运行结果如下:
a = 20000, b = 20000, c = -25536
a1 = -20000, b1 = -20000, c1 = 25536
- 为什么两个负数相加会变成整数?
- 为什么两个正数相加会变成负数?
- 这个结果是怎么计算出来的?
↓ ↓ ↓ \downarrow\downarrow\downarrow ↓↓↓
对于一个占用
w
w
w位内存的有符号整数
a
、
b
a、b
a、b,取值范围满足
−
2
w
−
1
≤
a
,
b
≤
2
w
−
1
−
1
-2^{w-1}\le a,b \le 2^{w-1}-1
−2w−1≤a,b≤2w−1−1
并且
a
、
b
a、b
a、b在内存中是采用补码表示的。
根据前置1
的说明,有符号整数的相加可以通过解释成无符号整数相加,然后在把结果按照有符号整数解释出来即可。根据补码的计算公式(可参照深入理解计算机中的整数)
a
=
−
x
w
−
1
×
2
w
−
1
+
∑
i
=
0
w
−
2
(
x
i
2
i
)
a=-x_{w-1}\times2^{w-1} + \sum_{i=0}^{w-2}(x_i2^i)
a=−xw−1×2w−1+i=0∑w−2(xi2i)
如果按照无符号整数解释
a
1
=
x
w
−
1
×
2
w
−
1
+
∑
i
=
0
w
−
2
(
x
i
2
i
)
=
a
+
x
w
−
1
2
w
−
1
+
x
w
−
1
2
w
−
1
=
a
+
x
w
−
1
2
w
\begin{aligned} a_1&=x_{w-1}\times2^{w-1} + \sum_{i=0}^{w-2}(x_i2^i)\\ &= a + x_{w-1}2^{w-1}+x_{w-1}2^{w-1} \\[1.5ex] &=a + x_{w-1}2^w\\ \end{aligned}
a1=xw−1×2w−1+i=0∑w−2(xi2i)=a+xw−12w−1+xw−12w−1=a+xw−12w
同理
b
1
=
b
+
x
w
−
1
2
w
b_1= b + x_{w-1}2^w
b1=b+xw−12w
⇒
a
1
+
b
1
=
a
+
x
w
−
1
2
w
+
b
+
x
w
−
1
2
w
\Rightarrow a_1+b_1= a+x_{w-1}2^w+b+ x_{w-1}2^w
⇒a1+b1=a+xw−12w+b+xw−12w
⇒
a
1
+
b
1
=
a
+
b
+
m
2
w
\Rightarrow a_1+b_1=a+b+m2^w
⇒a1+b1=a+b+m2w
-
如果 − 2 w ≤ a + b < − 2 w − 1 -2^w\le a+b<-2^{w-1} −2w≤a+b<−2w−1,此时 m > 0 m>0 m>0并且 0 ≤ a + b + 2 w < 2 w − 1 0\le a+b+2^w <2^{w-1} 0≤a+b+2w<2w−1
⇒ a 1 + b 1 = ( a + b + 2 w ) + ( m − 1 ) 2 w \Rightarrow a_1+b_1=(a+b+2^w)+(m-1)2^w ⇒a1+b1=(a+b+2w)+(m−1)2w
因为 a 1 + b 1 a_1+b_1 a1+b1是无符号整数
所以取模后 a 1 + b 1 = a + b + 2 w a_1+b_1 = a+b+2^w a1+b1=a+b+2w 并且 二进制的最高位为0
所以按照有符号位的解释方式计算最终结果也是 a + b + 2 w a+b+2^w a+b+2w -
如果 − 2 w − 1 ≤ a + b < 0 -2^{w-1}\le a+b<0 −2w−1≤a+b<0,此时 m > 0 m>0 m>0并且 2 w − 1 ≤ a + b + 2 w < 2 w 2^{w-1}\le a+b+2^w <2^{w} 2w−1≤a+b+2w<2w
⇒ a 1 + b 1 = ( a + b + 2 w ) + ( m − 1 ) 2 w \Rightarrow a_1+b_1=(a+b+2^w)+(m-1)2^w ⇒a1+b1=(a+b+2w)+(m−1)2w
因为 a 1 + b 1 a_1+b_1 a1+b1是无符号整数
所以取模后 a 1 + b 1 = a + b + 2 w a_1+b_1 = a+b+2^w a1+b1=a+b+2w 并且 二进制的最高位为1
按照有符号位的解释方式计算最终结果也是 a + b + 2 w − 2 w = a + b a+b+2^w-2^w = a+b a+b+2w−2w=a+b -
如果 0 ≤ a + b < 2 w − 1 , a 1 + b 1 = a + b 0\le a+b<2^{w-1}, a_1+b_1=a+b 0≤a+b<2w−1,a1+b1=a+b并且 二进制的最高位为0
所以按照有符号位的解释方式计算最终结果也是 a + b a+b a+b -
如果 2 w − 1 ≤ a + b < 2 w , a 1 + b 1 = a + b 2^{w-1}\le a+b<2^{w}, a_1+b_1=a+b 2w−1≤a+b<2w,a1+b1=a+b并且 二进制的最高位为1
按照有符号位的解释方式计算最终结果也是 a + b − 2 w = a + b − 2 w a+b-2^w = a+b-2^w a+b−2w=a+b−2w
根据上面四种情况,我们总结有符号整数的加法公式如下:
假设
a
a
a和
b
b
b都是占用
w
w
w位的有符号整数,则
a
+
b
a+b
a+b的运算结果如下:
c
=
{
a
+
b
−
2
w
,
a
+
b
≥
2
w
−
1
正溢出
a
+
b
,
−
2
w
−
1
≤
a
+
b
<
2
w
−
1
正常
a
+
b
+
2
w
,
a
+
b
<
−
2
w
−
1
负溢出
c= \begin{cases} a + b-2^w,&a+b\ge2^{w-1}&正溢出\\ a+b,&-2^{w-1}\le a+b<2^{w-1}&正常\\ a+b+2^w,&a+b <-2^{w-1}&负溢出 \end{cases}
c=⎩
⎨
⎧a+b−2w,a+b,a+b+2w,a+b≥2w−1−2w−1≤a+b<2w−1a+b<−2w−1正溢出正常负溢出
接下来,分析一下我们刚开始的例子:
a
=
20000
=
100111000100000
a = 20000=100 1110 0010 0000
a=20000=100111000100000
b
=
20000
=
100111000100000
b = 20000=100 1110 0010 0000
b=20000=100111000100000
a
+
b
=
40000
>
2
15
a+b=40000>2^{15}
a+b=40000>215, 正溢出
⇒
c
=
a
+
b
−
2
16
=
40000
−
65536
=
−
25536
\Rightarrow c = a+b-2^{16} = 40000-65 536=-25536
⇒c=a+b−216=40000−65536=−25536
a
1
=
−
20000
=
1111011000100000
a_1 = -20000=1111 0110 0010 0000
a1=−20000=1111011000100000
b
1
=
−
20000
=
1111011000100000
b_1 = -20000=1111 0110 0010 0000
b1=−20000=1111011000100000
a
1
+
b
1
=
−
40000
<
−
2
15
a_1+b_1=-40000<-2^{15}
a1+b1=−40000<−215, 负溢出
⇒
c
1
=
a
1
+
b
1
+
2
16
=
−
40000
+
65536
=
25536
\Rightarrow c_1 = a_1+b_1+2^{16} = -40000+65 536=25536
⇒c1=a1+b1+216=−40000+65536=25536
有符号整数加法溢出判断
假设
a
a
a和
b
b
b都是占用
w
w
w位的有符号整数,并且
c
=
a
+
b
c = a+b
c=a+b,
当且仅当
a
>
0
,
b
>
0
,
c
≤
0
a>0,b>0,c\le0
a>0,b>0,c≤0时,发生了正溢出
当且仅当
a
<
0
,
b
<
0
,
c
≥
0
a<0,b<0,c\ge0
a<0,b<0,c≥0时,发生了负溢出
无符号整数的乘法
有符号整数的乘法和加法一样,存在两种情况,一种是正常未溢出,一种是正溢出。而计算机的处理方式也是一致的,都是截断处理,所以我们直接给出有符号整数相乘的计算公式:
假设
a
a
a和
b
b
b都是占用
w
w
w位的无符号整数,则
a
×
b
a\times b
a×b的运算结果如下:
c
=
(
a
×
b
)
m
o
d
2
w
c=(a\times b)\mod 2^w
c=(a×b)mod2w
看下面的例子:
unsigned short a = 300;
unsigned short b = 300;
printf("a = %hu, b = %hu, c = %hu\n",a,b,a*b);
运行结果如下:
a = 300, b = 300, c = 24464
因为
c
=
a
×
b
=
300
×
300
=
90000
>
2
16
c = a\times b=300\times 300=90000>2^{16}
c=a×b=300×300=90000>216
所以
c
=
90000
−
2
16
=
90000
−
65536
=
24464
c = 90000-2^{16} = 90000-65536 = 24464
c=90000−216=90000−65536=24464
有符号整数的乘法
知道了无符号整数的乘法,根据前置2
的条件,我们可以很容易计算有符号整数的乘法
- 按照无符号解释有符号整数
- 无符号整数进行相乘,得到无符号整数,取模得到截断后的无符号整数
- 按照有符号数解释无符号结果,得到最终结果
还是看一个例子:
signed short a = -400;
signed short b = 300;
printf("a = %hd, b = %hd, c = %hd\n",a,b,a*b);
signed short a1 = -400;
signed short b1 = -300;
printf("a1 = %hd, b1 = %hd, c1 = %hd\n",a1,b1,a1*b1);
运行结果如下:
a = -400, b = 300, c = 11072
a1 = -400, b1 = -300, c1 = -11072
我们来分析一下:
a
=
−
400
a=-400
a=−400,无符号解释为
−
400
+
65536
=
65136
-400+65536=65136
−400+65536=65136
b
=
300
b=300
b=300,无符号解释为
300
300
300
c
1
=
a
×
b
=
65136
×
300
m
o
d
2
16
=
19540800
m
o
d
2
16
=
11072
c^1 = a\times b = 65136\times 300 \mod2^{16}= 19540800\mod2^{16} = 11072
c1=a×b=65136×300mod216=19540800mod216=11072
c
=
c
1
c = c^1
c=c1的有符号解释
11072
11072
11072
a
1
=
−
400
a_1=-400
a1=−400,无符号解释为
−
400
+
65536
=
65136
-400+65536=65136
−400+65536=65136
b
1
=
−
300
b_1=-300
b1=−300,无符号解释为
−
300
+
65536
=
65236
-300+65536 = 65236
−300+65536=65236
c
1
1
=
a
1
×
b
1
=
65136
×
65236
m
o
d
2
16
=
4249212096
m
o
d
2
16
=
54464
c_1^1 = a_1\times b_1 = 65136\times 65236 \mod2^{16}= 4249212096\mod2^{16} = 54464
c11=a1×b1=65136×65236mod216=4249212096mod216=54464
c
1
=
c
1
1
c_1 = c_1^1
c1=c11的有符号解释
54464
−
65536
=
−
11072
54464 - 65536=-11072
54464−65536=−11072
乘以常数的优化
通过前置3
,我们可以知道,如果乘法可以通过加法、减法、位级运算和移位
来实现,程序会运行的更好。
通常情况下,如果程序中存在乘以常数的代码,编译器在编译过程中通常会把代码转换为一系列加法、减法、位级运算和移位
的指令,我们下面就来感受一下该转换的魅力。
首先,如果我们将一个无符号整数乘以一个2的正次幂的常数,我们很容易通过移位获取到结果,比如:
a
×
2
k
a\times 2^k
a×2k,其中
a
,
k
a,k
a,k是无符号整数,我们通过
a
a
a<<
k
k
k即得到结果。
那么对于a是一个有符号整数呢,该方法还适用吗?
答案:适用。
因为每次左移其实是相当于乘以2,我们可以把有符号整数解释成无符号数,然后左移,然后在把结果解释成有符号数即可,也就是说移位的操作对无符号和有符号整数都适用,只不过数据的解释方式不一样,看下面的例子:
signed short a = -1;
printf("a = %hd, c = %hd\n",a,a<<1);
运行结果如下:
a = -1, c = -2
a
a
a在内存的表示为
0
b
1111111111111111
0b11111111 11111111
0b1111111111111111
a
a
a<<1后内存表示为
0
b
1111111111111110
0b11111111 11111110
0b1111111111111110
移动后的补码表示的数值为
c
=
−
2
c = -2
c=−2
到这里,我们终于发现了补码的魅力了,补码这个东西不影响加法、减法、乘法、位级运算和移位的结果,只影响解释方式,不影响机器级的表示。
接下来,考虑任意无符号整数常数 K K K,即将 a × K a\times K a×K转换为加法、减法和位级运算
- 第一种方式:我们可以遍历 K K K的每一位,如果为1,我们令 a a a<< n n n, n n n为当前位的位置,最后把每个移位后的值相加,这种方式最多会有 w ∗ 2 − 1 w*2-1 w∗2−1个周期的操作( w w w为 K K K占据的位数)
- 第二种方式:我们找出 K K K中连续为1的位数,比如开始位置 n n n,结束位置 m m m,我们可以通过 ( a (a (a<< ( n + 1 ) − a (n+1)-a (n+1)−a<< m ) m) m)三个周期就能得到结果
当然,这两种方式不一定比乘法来的效率高,所以具体还得看实际情况而定。
这里给出一个算法题:
// 提示:乘法运算可以转化为移位和加减运算的组合
// 假设进行一个乘法运算需要10个运行周期
// 假设加法、减法、移位、位运算需要1个周期
// 设计算法计算当一个整数乘以一个常数的时候最少需要几个周期
// val:常数值
// return:最小需要的周期数量
int multiOptimization(int val)
{
//todo
}
int multiOptimization(int val)
{
int len = sizeof(val) * 8;
int m=-1;
int n=-1;
int c1=0;
int c2=0;
for(int i = len-1;i>=0;i--)
{
if(((1<<i)&val)>0)
{
if(m>=0)
{
n=i;
}
else
{
m=n=i;
}
c2++;
}
else if(m>=0)
{
c1+=3;
m = n =-1;
}
}
if(m>=0)
{
c1+=3;
}
return c1<c2?(c1<10?c1:10):(c2<10?c2:10);
}
int main(int argc, const char * argv[])
{
// 251的二进制表示11111011
// 341的二进制表示101010101
// 87551的二进制表示10101010111111111
int a1 =multiOptimization(251);
int a2 =multiOptimization(341);
int a3 =multiOptimization(87551);
printf("a1 = %d\n", a1);
printf("a2 = %d\n", a2);
printf("a3 = %d\n", a3);
return 0;
}
运行结果为:
a1 = 6
a2 = 5
a3 = 10
无符号整数的除法
在大多数机器上,整数除法要比整数乘法更慢,需要30 个或者更多的时钟周期,除以2的幂也可以用移位运算来实现,只不过我们用的是右移,而不是左移。无符号和有符号整数分别使用逻辑移位
和算术移位
来达到目的。
- 逻辑左移:不考虑符号位,全部位都左移,低位用0补齐
- 逻辑右移:不考虑符号位,全部位都右移,高位用0补齐
- 算术左移:符号位不变,其余位都左移,低位用0补齐
- 算术右移:符号位不变,其余位都右移,高位用符号位补齐
接下来,先介绍一下向上取整和向下取整,因为整数的除法结果一定是整数。
向上取整
:取整结果是大于等于当前整数的最小整数,比如
⌈
3.14
⌉
=
4
\lceil 3.14\rceil=4
⌈3.14⌉=4
⌈
−
3.14
⌉
=
3
\lceil -3.14\rceil=3
⌈−3.14⌉=3
⌈
3
⌉
=
3
\lceil 3\rceil=3
⌈3⌉=3
向下取整
:取整结果是小于等于当前整数的最大整数,比如
⌊
3.14
⌋
=
3
\lfloor 3.14\rfloor=3
⌊3.14⌋=3
⌊
−
3.14
⌋
=
4
\lfloor -3.14\rfloor=4
⌊−3.14⌋=4
⌊
3
⌋
=
3
\lfloor 3\rfloor=3
⌊3⌋=3
计算机关于整数除法的规则为:如果结果大于0,向下取整,如果结果小于0,向上取整
对于无符号整数除以2的无符号整数次幂是非常容易理解的,因为不涉及算术移位
对于
a
÷
2
k
a\div 2^k
a÷2k,其中
a
,
k
a,k
a,k是无符号整数,我们通过
a
a
a>>
k
k
k即得到结果,当然这个结果一定是向下取整了,这里不举例子了。
有符号整数的除法
对于有符号整数 a a a除以2的无符号整数次幂,我们有两种取整方式:
- 当前 a ≥ 0 a\ge 0 a≥0时,向下取整。
- 当前 a < 0 a<0 a<0时,向上取整。
根据不同的取整方式,有不同的处理方法:
一种是比较好想到的,采用算数右移的方式
对于
a
÷
2
k
a\div 2^k
a÷2k,其中
a
a
a是有符号整数,
k
k
k是无符号整数,我们通过
a
a
a>>
k
k
k(算术移位)即得到结果,该结果是向下取整的。
证明:
对于
a
≥
0
a\ge 0
a≥0,
a
a
a表现出来就是一个无符号整数,所以满足向下取整
对于
a
<
0
a<0
a<0,
a
a
a算数右移k位之后
a
1
a_1
a1的位级表示成
[
1
,
1
,
1
⋯
,
x
w
−
1
,
x
w
−
2
,
⋯
,
x
k
]
[1,1,1\cdots,x_{w-1},x_{w-2},\cdots,x_k]
[1,1,1⋯,xw−1,xw−2,⋯,xk]
⇒
a
1
=
∑
i
=
k
i
=
w
−
1
(
x
i
∗
2
i
−
k
)
−
2
w
−
k
\Rightarrow a_1=\sum_{i=k}^{i=w-1}(x_i*2^{i-k})-2^{w-k}
⇒a1=i=k∑i=w−1(xi∗2i−k)−2w−k
⇒
a
1
2
k
=
∑
i
=
k
i
=
w
−
1
(
x
i
∗
2
i
)
−
2
w
\Rightarrow a_12^{k}=\sum_{i=k}^{i=w-1}(x_i*2^{i})-2^{w}
⇒a12k=i=k∑i=w−1(xi∗2i)−2w
⇒
a
1
2
k
=
a
−
∑
i
=
0
i
=
k
−
1
(
x
i
∗
2
i
)
\Rightarrow a_12^{k}=a-\sum_{i=0}^{i=k-1}(x_i*2^{i})
⇒a12k=a−i=0∑i=k−1(xi∗2i)
假设
b
=
∑
i
=
0
i
=
k
−
1
(
x
i
∗
2
i
)
b = \sum_{i=0}^{i=k-1}(x_i*2^{i})
b=∑i=0i=k−1(xi∗2i),则
0
≤
b
<
2
k
0\le b<2^k
0≤b<2k
⇒
a
=
a
1
2
k
+
b
\Rightarrow a = a_12^k+b
⇒a=a12k+b
⇒
a
÷
2
k
=
a
1
+
b
÷
2
k
\Rightarrow a\div 2^k = a_1+b\div 2^k
⇒a÷2k=a1+b÷2k
因为舍弃的部分
b
÷
2
k
b\div 2^k
b÷2k大于0,所以是向下取整
另一种方式是对于
a
÷
2
k
a\div 2^k
a÷2k,其中
a
a
a是有符号整数,
k
k
k是无符号整数,我们通过
(
a
+
(
1
(a+(1
(a+(1<<
k
)
−
1
)
k)-1)
k)−1)>>
k
k
k(算术移位)即得到结果,该结果是向上取整的。
这个听起来很玄幻,其实原理很简单,就是给
a
a
a添加一个偏移量,然后在采用第一种方式。
很简单的一个同理的问题就是:
int a = 10;
int b = 9;
计算a/b向上取整?
就是比如
a=9,a/b=1
a=10,a/b=2
这种问题一般我们的求解方法就是
int c = (a+b-1)/b;
( a + ( 1 (a+(1 (a+(1<< k ) − 1 ) k)-1) k)−1)>> k k k跟这个是一样的。
根据计算机除法原理:如果结果大于0,向下取整,如果结果小于0,向上取整
,我们得出2次幂除法公式应该是:
(
a
<
0
?
(
a
+
(
1
<
<
k
)
−
1
)
:
a
)
>
>
k
(a<0?(a+(1<<k)-1):a)>>k
(a<0?(a+(1<<k)−1):a)>>k