使用按位移位操作对扩展进行签名
Sign extension with bitwise shift operation
在问答之后,我试图检查答案,所以我写道:
#include <stdio.h>
int main ()
{
int t;int i;
for (i=120;i<140;i++){
t = (i - 128) >> 31;
printf ("t = %X , i-128 = %X , ~t & i = %X , ~t = %X n", t, i-128 , (~t &i), ~t);
}
return 0;
}
输出为:
t = FFFFFFFF , i-128 = FFFFFFF8 , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFF9 , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFA , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFB , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFC , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFD , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFE , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFF , ~t & i = 0 , ~t = 0
t = 0 , i-128 = 0 , ~t & i = 80 , ~t = FFFFFFFF
t = 0 , i-128 = 1 , ~t & i = 81 , ~t = FFFFFFFF
t = 0 , i-128 = 2 , ~t & i = 82 , ~t = FFFFFFFF
t = 0 , i-128 = 3 , ~t & i = 83 , ~t = FFFFFFFF
t = 0 , i-128 = 4 , ~t & i = 84 , ~t = FFFFFFFF
t = 0 , i-128 = 5 , ~t & i = 85 , ~t = FFFFFFFF
t = 0 , i-128 = 6 , ~t & i = 86 , ~t = FFFFFFFF
t = 0 , i-128 = 7 , ~t & i = 87 , ~t = FFFFFFFF
t = 0 , i-128 = 8 , ~t & i = 88 , ~t = FFFFFFFF
t = 0 , i-128 = 9 , ~t & i = 89 , ~t = FFFFFFFF
t = 0 , i-128 = A , ~t & i = 8A , ~t = FFFFFFFF
t = 0 , i-128 = B , ~t & i = 8B , ~t = FFFFFFFF
如果声明为整数,为什么~t
任何负数t
都会-1 == 0xFFFFFFFF
?
发件人:C 中的右移负数
编辑:根据最新标准草案的第6.5.7节,负数的这种行为取决于实现:
E1>>E2的结果是E1右移E2位位置。如果 E1 具有无符号类型或 E1 具有有符号类型和非负值,则结果的值是 E1/2E2 商的整数部分。如果 E1 具有有符号类型和负值,则结果值由实现定义。
而且,您的实现可能正在对 2 的补码数进行算术移位
运算符>>
为有符号右移位或算术右移位,将所有位向右移动指定的次数。重要的是>>
在移位后将最左边的符号位(最高有效位 MSB(填充到最左边的位。这称为符号扩展,用于在负数向右移动时保留负数的符号。
下面是我的图表表示,其中包含一个示例来显示它是如何工作的(对于一个字节(:
例:
i = -5 >> 3; shift bits right three time
五合二的补码形式是1111 1011
记忆表示:
MSB
+----+----+----+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
+----+----+----+---+---+---+---+---+
7 6 5 4 3 2 1 0
^ This seventh, the left most bit is SIGN bit
下面是,>>
是如何工作的?当你做-5 >> 3
this 3 bits are shifted
out and loss
MSB (___________)
+----+----+----+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
+----+----+----+---+---+---+---+---+
|
| ------------| ----------|
| | |
▼ ▼ ▼
+----+----+----+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
+----+----+----+---+---+---+---+---+
(______________)
The sign is
propagated
注意:最左边的三个位是一个,因为每个移位符号位都被保留,每个位也是正确的。我已经写了符号被传播,因为这三个位都是因为符号(但不是数据(。
[答案]
在您的输出中
前八行
~t is 0
==> t is FFFFFFFF
==> t is -1
(注:-1的2补码是FFFFFFFF
的,因为1 = 00000001
,1的补码是FFFFFFFE
的,而2的补码=1的补码+1即:FFFFFFFE
+00000001
=FFFFFFFF
(
因此,t
总是在循环的前八次中-1
评估。是的,如何?
在 for 循环中
for (i=120;i<140;i++){
t = (i - 128) >> 31;
前八次的i
值i = 120, 121, 122, 123, 124, 125, 126 ,127
所有八次值都小于 128。所以(i - 128) = -8, -7, -6, -5, -4, -3, -2, -1
的回归.因此,在前八次表示t = (i - 128) >> 31
移位权为负数。
t = (i - 128) >> 31
t = -ve number >> 31
因为在您的系统中 int 是 4 字节 = 32 位,所以大多数31
位都是移出和丢失的,并且由于对负数1
的符号位的传播,所有位值都变得1
。(如上图所示,一个字节(
所以拳头八次:
t = -ve number >> 31 == -1
t = -1
and this gives
~t = 0
因此,~t 的拳头输出八次为 0。
对于剩余的最后一行
~t is FFFFFFFF
==> ~t is -1
==> t is 0
对于剩余的最后一行,在 for 循环中
for (i=120;i<140;i++){
t = (i - 128) >> 31;
i 值为 128, 129, 130, 132, 133, 134, 135, 136, 137, 138, 139,
所有值都大于或等于 128。 并且符号位为 0
。
所以 (i - 128( 对于剩余的最后一行是 >=0
并且对于所有这些 MSB 符号位 = 0
.而且因为你再次向右移动 31 次,所有位除外,然后叹息位移出和符号位0
传播并用0
填充所有位,幅度变得0
.
我认为如果我也为正数写一个例子会很好。所以我们举一个例子5 >> 3
,五是一个字节是0000 0101
this 3 bits are shifted
out and loss
MSB (___________)
+----+----+----+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
+----+----+----+---+---+---+---+---+
|
| ------------| ----------|
| | |
▼ ▼ ▼
+----+----+----+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+----+----+----+---+---+---+---+---+
(______________)
The sign is
propagated
再看我写 符号是传播的,所以最左边的三个零是由于符号位。
因此,这就是运算符>>
Signed 右移所做的,并保留左操作数的符号。
为什么 t = (i - 128(>> 31 为每个数字给出零或 -1?
当一个非负 32 位整数向右移动 31 位时,所有非零位都会移出,最高有效位会用 0 填充,所以你最终得到 0。
通常,当负 32 位整数向右移动 31 位时,最高有效位不会用 0 填充,而是设置为数字符号,因此符号传播到所有位中,在 2 的补码表示中,所有设置为 1 的位都等于 -1。净效果就像你反复将数字除以 2,但有点扭曲......结果四舍五入为 -无穷大,而不是朝向 0。例如-2>>1==-1
但-3>>1==-2
和-5>>1==-3
。这称为算术右移。
当我说"通常"时,我的意思是 C 标准允许负值右移的几种不同行为。最重要的是,它允许有符号整数的非 2 补码表示。但是,通常您有 2 的补码表示和我上面显示/解释的行为。
Becaue t
要么是 0 要么是 -1,~t 也总是 -1 或 0。
这是由于(实现定义的(行为或(i - 128) >> 31
,它基本上复制了(i-128(的顶部位[假设32位整数]。如果i
> 128,则会导致顶部位为零。如果i
小于 128,则结果为负数,因此设置了顶部位。
由于~t
是"与t
相反的所有位",因此您可以期望如果t
为零,则始终t
0xffffffff。
>>
运算符右移是大多数编译器中的算术右移,意思是除以 2。
因此,如果,例如 int i ==-4
(0xfffffffc(,然后i>>1 == -2
(0xfffffffe(。
话虽如此,我建议您检查代码的程序集。
例如,x86有2个独立的指令 - shr
和sar
,分别表示逻辑移位和算术移位。
通常,编译器对无符号变量使用shr
(逻辑移位(和对有符号变量使用sar
(算术移位(。
下面是用gcc -S
生成的 C 代码和相应的程序集:
交流:
int x=10;
unsigned int y=10;
int main(){
unsigned int z=(x>>1)+(y>>1);
return 0;
}
A.S:
.file "a.c"
.globl x
.data
.align 4
.type x, @object
.size x, 4
x:
.long 10
.globl y
.align 4
.type y, @object
.size y, 4
y:
.long 10
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl x, %eax
sarl %eax ; <~~~~~~~~~~~~~~~~ Arithmetic shift, for signed int
movl y, %edx
shrl %edx ; <~~~~~~~~~~~~~~~~ Logical shift, for unsigned int
addl %edx, %eax
movl %eax, -4(%ebp)
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
.section .note.GNU-stack,"",@progbits
C 和 C++ 中的规则是负值右移的结果是定义实现。因此,请阅读编译器的文档。你得到的各种解释是有效的方法,但这些都不是语言定义强制要求的。
- 为什么在popback()操作之后,它仍然打印完整的矢量
- 重载操作程序时出错>>用于类中的字符串 memebr
- 是否可以通过C++扩展强制多个python进程共享同一内存
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 如何将这个C++哈希表转换为动态扩展和收缩,而不是使用硬设置的最大值
- 扩展光电二极管探测器以支持多个传感器
- 对字符串进行位操作
- 我可以在 C++ 中的函数体之外进行操作吗?
- MPI突然停止了对多个核心的操作
- 如何在信号处理程序和普通函数中对全局变量进行互斥读写操作
- 对字符数组中的元素执行逐位操作
- 如何在directx/c++中进行平移/缩放操作
- 逐位操作的隐式类型转换
- 为什么一个向量上的多线程操作很慢
- C++中的VLA,扩展名为std=C++11
- OpenGL 和 GLM 矩阵无法正确扩展,总是按比例缩小
- 排序时无法执行交换操作.我做的时候它会崩溃.为什么
- Python扩展在操作大列表时会创建无效指针
- 使用按位移位操作对扩展进行签名
- 在python扩展中操作来自SWIG的共享指针