按位 c - 在 for 循环内不同的结果(不知道如何放置更好)
bitwise c - inside a for loop different result (don't know how to put is better)
int bitcheck(int test){
int result = 0, unit = 0, i;
for (i = 0; i < 8; i++){
unit = test >> i & 1;
result |= unit << (8 - i - 1) + result;
}
return result;
}
假设测试 = 77 (0100 1101)
当i = 2时,单位等于1,结果(在"result |=.."之前)等于128。所以1 << 133(在计算"result | outcome"之前)怎么会是0010 0000 ? (我打印了每一步),但如果我自己做 1<<133 结果是 0000 0000?
我知道为什么1<<133的结果是0000 0000,但是为什么在for循环里不一样?
test is: 01001101
unit when i is 0: 00000001
00000001<<00000111 = 10000000
result when i is 0: 10000000
unit when i is 1: 00000000
00000000<<10000110 = 00000000
result when i is 1: 10000000
unit when i is 2: 00000001
00000001<<10000101 = 00100000
result when i is 2: 10100000
unit when i is 3: 00000001
00000001<<10100100 = 00010000
result when i is 3: 10110000
unit when i is 4: 00000000
00000000<<10110011 = 00000000
result when i is 4: 10110000
unit when i is 5: 00000000
00000000<<10110010 = 00000000
result when i is 5: 10110000
unit when i is 6: 00000001
00000001<<10110001 = 00000000
result when i is 6: 10110000
unit when i is 7: 00000000
00000000<<10110000 = 00000000
result when i is 7: 10110000
return result: 10110000
一个negative shift count or one larger than or equal to the bitsize of the left argument results in undefined behaviour.
因此,您不能期望结果为 0,但要注意 demons flying out of your nose。
此外,右移负值的有符号整数是实现定义的。 IOW,这留给你 compiler/toolchain 会发生什么。
根据您的结果:
00000001<<00000111 gets you 10000000,
which should be 1<<00000111
00000001<<10000101 gets you 00100000,
which should be 1<<00000101
00000001<<10100100 gets you 00010000,
which should be 1<<00000100
00000001<<10110001 gets you 00000000,
which could be 1<<00010001
我在这里检测到模式了吗?
shift-expression << additive-expression
如果additive-expression
为负数或additive-expression
大于或等于数组中的位数,则移位运算的结果为未定义 (升级)shift-expression
.
unsigned int int1 = 4;
unsigned int int2 = int1 << -3; // C4293: '<<' : shift count negative or too big, undefined behavior
unsigned int int4 = int1 << 32; // C4293: '<<' : shift count negative or too big, undefined behavior
以上例子来自VS 2013, section: Additional Details。虽然示例代码是用 C++ 编写的,但在 C 中也是相同的。
so how come 1 << 133 (before the calculation "result | outcome") is 0010 0000?
是0010 0000
而不是0000 0000
因为Undefined behavior。可能下次你会得到另一个值。您只是无法定义 未定义的行为。
正如其他人所指出的,这是 undefined behavior,这意味着如果 C 标准是您必须遵循的全部,理论上任何事情都可能发生。
我认为这对您的问题有点无益,因为您想知道为什么对相同的两个数字进行相同的操作会产生不同的结果。毕竟,虽然 C 标准没有指定结果是什么,但 CPU 本身确实遵循其自己的移位指令应该如何工作的规范,并且不太可能涉及生成随机结果。
您看到的差异很可能是由于 CPU 的移位实现与编译器在预计算编译时已知结果时使用的实现之间的差异。例如考虑这个简单的例子:
$ cat shift.c
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
int main(int argc, char ** argv)
{
uint32_t a = atoi(argv[1]), b = atoi(argv[2]), c, d;
c = a << b;
d = 1u << 133u;
printf("%08x << %08x = %08x\n",a,b,c);
printf("%08x << %08x = %08x\n",1,133,d);
return 0;
}
$ gcc -o shift{,.c}
shift.c: In function ‘main’:
shift.c:9:2: warning: left shift count >= width of type
$ ./shift 1 133
00000001 << 00000085 = 00000020
00000001 << 00000085 = 00000000
这是在 Intel Core2 Duo cpu 上 运行。看似相同的操作在这里执行了两次却得到了不同的结果。但是这两个操作实际上并不等同。第一个涉及编译器不知道其值的数字,因此它们被简单地编译成 sall
x86 指令。这条指令only uses the lowest 5 bits of the count。 133 (0x85) 的最低 5 位是 5,因此实际执行的位移是 1 << 5 = 32 (0x20),这与输出匹配。所以这是有道理的。
但是另一条线发生了什么?这里编译器知道两个操作数是什么,所以它可以预先计算结果并存储它。没有生成 sall
指令。 gcc 的位移位实现(仅当结果可以在编译时计算时才起作用)处理太大计数的方式与 sall
不同。在这种情况下,有效地使用了整个计数,并且所有位都被移走了。
总结一下:
- 非法计数导致未定义的行为
- 编译时已知的值的未定义移位暴露了编译器对未定义操作的实现。
- 在编译时不是的值的未定义移位暴露了CPU未定义操作的实现。
- 第 2 点和第 3 点是您看到位移结果不一致的原因。
添加括号以获得所需的操作顺序后
注意:当需要修复运行时问题时,post 可执行代码。
我认为问题的根源是
1) using undefined behaviour due to the 'too large' shift amounts
2) ignoring the precedence of operators in C
这是我使用的代码:
#include <stdio.h>
int main( void )
{
int test = 0x4d;
int result = 0;
int unit = 0;
int i;
printf( "test=%d\n", test );
for (i = 0; i < 8; i++)
{
unit = (test >> i) & 1;
printf( "unit=%d\n", unit);
result |= (unit << (8 - i - 1)) + result;
printf( "i=%d, result=%x\n", i, result );
}
return result;
}
结果如下:
test=77
unit=1
i=0, result=80
unit=0
i=1, result=80
unit=1
i=2, result=a0
unit=1
i=3, result=b0
unit=0
i=4, result=b0
unit=0
i=5, result=b0
unit=1
i=6, result=b2
unit=0
i=7, result=b2
------------------
(program exited with code: 178)
int bitcheck(int test){
int result = 0, unit = 0, i;
for (i = 0; i < 8; i++){
unit = test >> i & 1;
result |= unit << (8 - i - 1) + result;
}
return result;
}
假设测试 = 77 (0100 1101)
当i = 2时,单位等于1,结果(在"result |=.."之前)等于128。所以1 << 133(在计算"result | outcome"之前)怎么会是0010 0000 ? (我打印了每一步),但如果我自己做 1<<133 结果是 0000 0000?
我知道为什么1<<133的结果是0000 0000,但是为什么在for循环里不一样?
test is: 01001101
unit when i is 0: 00000001
00000001<<00000111 = 10000000
result when i is 0: 10000000
unit when i is 1: 00000000
00000000<<10000110 = 00000000
result when i is 1: 10000000
unit when i is 2: 00000001
00000001<<10000101 = 00100000
result when i is 2: 10100000
unit when i is 3: 00000001
00000001<<10100100 = 00010000
result when i is 3: 10110000
unit when i is 4: 00000000
00000000<<10110011 = 00000000
result when i is 4: 10110000
unit when i is 5: 00000000
00000000<<10110010 = 00000000
result when i is 5: 10110000
unit when i is 6: 00000001
00000001<<10110001 = 00000000
result when i is 6: 10110000
unit when i is 7: 00000000
00000000<<10110000 = 00000000
result when i is 7: 10110000
return result: 10110000
一个negative shift count or one larger than or equal to the bitsize of the left argument results in undefined behaviour.
因此,您不能期望结果为 0,但要注意 demons flying out of your nose。
此外,右移负值的有符号整数是实现定义的。 IOW,这留给你 compiler/toolchain 会发生什么。
根据您的结果:
00000001<<00000111 gets you 10000000,
which should be 1<<00000111
00000001<<10000101 gets you 00100000,
which should be 1<<00000101
00000001<<10100100 gets you 00010000,
which should be 1<<00000100
00000001<<10110001 gets you 00000000,
which could be 1<<00010001
我在这里检测到模式了吗?
shift-expression << additive-expression
如果additive-expression
为负数或additive-expression
大于或等于数组中的位数,则移位运算的结果为未定义 (升级)shift-expression
.
unsigned int int1 = 4;
unsigned int int2 = int1 << -3; // C4293: '<<' : shift count negative or too big, undefined behavior
unsigned int int4 = int1 << 32; // C4293: '<<' : shift count negative or too big, undefined behavior
以上例子来自VS 2013, section: Additional Details。虽然示例代码是用 C++ 编写的,但在 C 中也是相同的。
so how come 1 << 133 (before the calculation "result | outcome") is 0010 0000?
是0010 0000
而不是0000 0000
因为Undefined behavior。可能下次你会得到另一个值。您只是无法定义 未定义的行为。
正如其他人所指出的,这是 undefined behavior,这意味着如果 C 标准是您必须遵循的全部,理论上任何事情都可能发生。
我认为这对您的问题有点无益,因为您想知道为什么对相同的两个数字进行相同的操作会产生不同的结果。毕竟,虽然 C 标准没有指定结果是什么,但 CPU 本身确实遵循其自己的移位指令应该如何工作的规范,并且不太可能涉及生成随机结果。
您看到的差异很可能是由于 CPU 的移位实现与编译器在预计算编译时已知结果时使用的实现之间的差异。例如考虑这个简单的例子:
$ cat shift.c
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
int main(int argc, char ** argv)
{
uint32_t a = atoi(argv[1]), b = atoi(argv[2]), c, d;
c = a << b;
d = 1u << 133u;
printf("%08x << %08x = %08x\n",a,b,c);
printf("%08x << %08x = %08x\n",1,133,d);
return 0;
}
$ gcc -o shift{,.c}
shift.c: In function ‘main’:
shift.c:9:2: warning: left shift count >= width of type
$ ./shift 1 133
00000001 << 00000085 = 00000020
00000001 << 00000085 = 00000000
这是在 Intel Core2 Duo cpu 上 运行。看似相同的操作在这里执行了两次却得到了不同的结果。但是这两个操作实际上并不等同。第一个涉及编译器不知道其值的数字,因此它们被简单地编译成 sall
x86 指令。这条指令only uses the lowest 5 bits of the count。 133 (0x85) 的最低 5 位是 5,因此实际执行的位移是 1 << 5 = 32 (0x20),这与输出匹配。所以这是有道理的。
但是另一条线发生了什么?这里编译器知道两个操作数是什么,所以它可以预先计算结果并存储它。没有生成 sall
指令。 gcc 的位移位实现(仅当结果可以在编译时计算时才起作用)处理太大计数的方式与 sall
不同。在这种情况下,有效地使用了整个计数,并且所有位都被移走了。
总结一下:
- 非法计数导致未定义的行为
- 编译时已知的值的未定义移位暴露了编译器对未定义操作的实现。
- 在编译时不是的值的未定义移位暴露了CPU未定义操作的实现。
- 第 2 点和第 3 点是您看到位移结果不一致的原因。
添加括号以获得所需的操作顺序后
注意:当需要修复运行时问题时,post 可执行代码。
我认为问题的根源是
1) using undefined behaviour due to the 'too large' shift amounts
2) ignoring the precedence of operators in C
这是我使用的代码:
#include <stdio.h>
int main( void )
{
int test = 0x4d;
int result = 0;
int unit = 0;
int i;
printf( "test=%d\n", test );
for (i = 0; i < 8; i++)
{
unit = (test >> i) & 1;
printf( "unit=%d\n", unit);
result |= (unit << (8 - i - 1)) + result;
printf( "i=%d, result=%x\n", i, result );
}
return result;
}
结果如下:
test=77
unit=1
i=0, result=80
unit=0
i=1, result=80
unit=1
i=2, result=a0
unit=1
i=3, result=b0
unit=0
i=4, result=b0
unit=0
i=5, result=b0
unit=1
i=6, result=b2
unit=0
i=7, result=b2
------------------
(program exited with code: 178)