我真的不知道为什么移位在一种情况下有效但在另一种情况下无效
I don't really know why bit-shifting works in one scenario but not the other
所以我正在做一些位移,我遇到了以下问题,如果能得到答案,我将不胜感激:
作为参数,我可以传递 1 个字节的大小。
前4位代表一个分子。
最后4位代表分母。
以下代码有效并给出正确的输出:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(int argc, char** argv)
{
for(int i = 1; i < argc; i++)
{
unsigned char numerator = atoi(argv[i]);
numerator = (numerator >> 4);
numerator = (numerator << 4);
unsigned char denominator = atoi(argv[i]);
denominator = (denominator << 4);
denominator = (denominator >> 4);
printf("%d/%d\n", numerator, denominator);
}
return 0;
}
但是如果我像这样替换位移部分,分母给出与分子相同的输出:
unsigned char numerator = atoi(argv[i]);
numerator = (numerator >> 4) << 4;
unsigned char denominator = atoi(argv[i]);
denominator = (denominator << 4) >> 4;
示例输入为:
./test 1
./test 16
给出的输出:
0/1
16/16
预期输出:
0/1
16/0
在此先感谢您的帮助。
似乎是将值转换为更大的整数类型(例如int),并且在执行移位时,高位保留为更大的int类型,然后将结果转换回两个班次都完成后的 unsigned char。
我会使用这样的位掩码,而不是移位:
...
unsigned char data = atoi(argv[i]);
unsigned char numerator = data & 0xf0;
unsigned char denominator = data & 0xf;
...
问题案例是分母,变量的大小是分母的一个因素。在第一个版本中,您有:
denominator = (denominator << 4);
denominator = (denominator >> 4);
这会将值左移 4,然后将其截断以适合 unsigned char
,然后右移结果。在 16
的情况下,第一个移位产生 256
,然后在存储在分母中时将其截断为 0
。然后右移只是移动 0
.
在第二个版本中,您有:
denominator = (denominator << 4) >> 4;
这会将其左移 4,但不会截断结果以适应 unsigned char
,然后右移未截断的结果。在16
的情况下,第一个移位产生256
,然后右移回到16
。
要获得与第一个版本等效的一行,您可以这样做:
denominator = (unsigned char)(denominator << 4) >> 4;
在 C 中计算算术表达式时,任何小于 int
的整数类型都会在执行计算之前提升为 int
。
所以这段代码:
unsigned char denominator = atoi(argv[i]);
denominator = (denominator << 4);
denominator = (denominator >> 4);
结果如下(每个方框代表4位,方框内的文字为16进制数字):
请注意,赋值 denominator = (denominator << 4);
会强制编译器将值转换回 unsigned char
。然后在下一行代码中,编译器需要将 unsigned char
提升回 int
.
但是这段代码:
unsigned char denominator = atoi(argv[i]);
denominator = (denominator << 4) >> 4;
跳过转换回 unsigned char
和第二次升级到 int
。所以顺序是:
请注意,最终值与原始值相同,因为移位是使用 32 位执行的,并且没有从 32 位值的左侧移出任何内容。
分子没有同样的问题。因为分子先右移,所以右边的位丢失了。升级到 int
对结果没有影响。这是分子的顺序:
所以我正在做一些位移,我遇到了以下问题,如果能得到答案,我将不胜感激:
作为参数,我可以传递 1 个字节的大小。
前4位代表一个分子。 最后4位代表分母。
以下代码有效并给出正确的输出:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(int argc, char** argv)
{
for(int i = 1; i < argc; i++)
{
unsigned char numerator = atoi(argv[i]);
numerator = (numerator >> 4);
numerator = (numerator << 4);
unsigned char denominator = atoi(argv[i]);
denominator = (denominator << 4);
denominator = (denominator >> 4);
printf("%d/%d\n", numerator, denominator);
}
return 0;
}
但是如果我像这样替换位移部分,分母给出与分子相同的输出:
unsigned char numerator = atoi(argv[i]);
numerator = (numerator >> 4) << 4;
unsigned char denominator = atoi(argv[i]);
denominator = (denominator << 4) >> 4;
示例输入为:
./test 1
./test 16
给出的输出:
0/1
16/16
预期输出:
0/1
16/0
在此先感谢您的帮助。
似乎是将值转换为更大的整数类型(例如int),并且在执行移位时,高位保留为更大的int类型,然后将结果转换回两个班次都完成后的 unsigned char。
我会使用这样的位掩码,而不是移位:
...
unsigned char data = atoi(argv[i]);
unsigned char numerator = data & 0xf0;
unsigned char denominator = data & 0xf;
...
问题案例是分母,变量的大小是分母的一个因素。在第一个版本中,您有:
denominator = (denominator << 4);
denominator = (denominator >> 4);
这会将值左移 4,然后将其截断以适合 unsigned char
,然后右移结果。在 16
的情况下,第一个移位产生 256
,然后在存储在分母中时将其截断为 0
。然后右移只是移动 0
.
在第二个版本中,您有:
denominator = (denominator << 4) >> 4;
这会将其左移 4,但不会截断结果以适应 unsigned char
,然后右移未截断的结果。在16
的情况下,第一个移位产生256
,然后右移回到16
。
要获得与第一个版本等效的一行,您可以这样做:
denominator = (unsigned char)(denominator << 4) >> 4;
在 C 中计算算术表达式时,任何小于 int
的整数类型都会在执行计算之前提升为 int
。
所以这段代码:
unsigned char denominator = atoi(argv[i]);
denominator = (denominator << 4);
denominator = (denominator >> 4);
结果如下(每个方框代表4位,方框内的文字为16进制数字):
请注意,赋值 denominator = (denominator << 4);
会强制编译器将值转换回 unsigned char
。然后在下一行代码中,编译器需要将 unsigned char
提升回 int
.
但是这段代码:
unsigned char denominator = atoi(argv[i]);
denominator = (denominator << 4) >> 4;
跳过转换回 unsigned char
和第二次升级到 int
。所以顺序是:
请注意,最终值与原始值相同,因为移位是使用 32 位执行的,并且没有从 32 位值的左侧移出任何内容。
分子没有同样的问题。因为分子先右移,所以右边的位丢失了。升级到 int
对结果没有影响。这是分子的顺序: