tyt*_*yty 7 c standards bit-shift zero negative-number
所有这些功能都可以在我的机器上获得预期的结果.他们都在其他平台上工作吗?
更具体地说,如果x在1的补码机器上具有位表示0xffffffff或在有符号的幅度机器上具有0x80000000,那么标准对于(无符号)x的表示有何看法?
另外,我认为v2,v2a,v3,v4中的(unsigned)转换是多余的.它是否正确?
假设sizeof(int)= 4且CHAR_BIT = 8
int logicalrightshift_v1 (int x, int n) {
return (unsigned)x >> n;
}
int logicalrightshift_v2 (int x, int n) {
int msb = 0x4000000 << 1;
return ((x & 0x7fffffff) >> n) | (x & msb ? (unsigned)0x80000000 >> n : 0);
}
int logicalrightshift_v2a (int x, int n) {
return ((x & 0x7fffffff) >> n) | (x & (unsigned)0x80000000 ? (unsigned)0x80000000 >> n : 0);
}
int logicalrightshift_v3 (int x, int n) {
return ((x & 0x7fffffff) >> n) | (x < 0 ? (unsigned)0x80000000 >> n : 0);
}
int logicalrightshift_v4 (int x, int n) {
return ((x & 0x7fffffff) >> n) | (((unsigned)x & 0x80000000) >> n);
}
int logicalrightshift_v5 (int x, int n) {
unsigned y;
*(int *)&y = x;
y >>= n;
*(unsigned *)&x = y;
return x;
}
int logicalrightshift_v6 (int x, int n) {
unsigned y;
memcpy (&y, &x, sizeof (x));
y >>= n;
memcpy (&x, &y, sizeof (x));
return x;
}
Run Code Online (Sandbox Code Playgroud)
如果x在1的补码机器上具有位表示0xffffffff或在有符号量值的机器上具有0x80000000,那么标准对于(无符号)x的表示有何看法?
转换为以值而非表示形式unsigned指定.如果你转换成,你总是得到(所以如果你是32位,你总是得到).无论您的实现使用的有符号数的表示如何,都会发生这种情况.-1unsignedUINT_MAXunsigned4294967295
同样,如果你转换-0为unsigned那么你总是得到0. -0在数值上等于0.
注意,不需要补码或符号幅度实现来支持负零; 如果没有,则访问此类表示会导致程序具有未定义的行为.
逐个完成您的功能:
int logicalrightshift_v1(int x, int n)
{
return (unsigned)x >> n;
}
Run Code Online (Sandbox Code Playgroud)
负值的此函数的结果x将取决于UINT_MAX,并且如果(unsigned)x >> n不在范围内,则将进一步实现定义int.例如,无论机器用于签名数字的表示形式如何,logicalrightshift_v1(-1, 1)都将返回该值UINT_MAX / 2.
int logicalrightshift_v2(int x, int n)
{
int msb = 0x4000000 << 1;
return ((x & 0x7fffffff) >> n) | (x & msb ? (unsigned)0x80000000 >> n : 0);
}
Run Code Online (Sandbox Code Playgroud)
几乎所有关于此的内容都可以是实现定义的.假设您尝试msb在符号位中创建一个值为1并且在值位中为零,则不能通过使用移位来移植 - 您可以使用~INT_MAX,但是这允许在符号幅度上具有未定义的行为不允许负零的机器,允许在两个补码机器上给出实现定义的结果.
类型0x7fffffff和0x80000000将取决于各种类型的范围,这将影响此表达式中其他值的提升方式.
int logicalrightshift_v2a(int x, int n)
{
return ((x & 0x7fffffff) >> n) | (x & (unsigned)0x80000000 ? (unsigned)0x80000000 >> n : 0);
}
Run Code Online (Sandbox Code Playgroud)
如果创建的unsigned值不在范围内int(例如,给定32位int,值> 0x7fffffff),则return语句中的隐式转换将生成实现定义的值.这同样适用于v3和v4.
int logicalrightshift_v5(int x, int n)
{
unsigned y;
*(int *)&y = x;
y >>= n;
*(unsigned *)&x = y;
return x;
}
Run Code Online (Sandbox Code Playgroud)
这仍然是实现定义的,因为未指定表示中的符号位是否int对应于表示中的值位或填充位unsigned.如果它对应于填充位,则它可以是陷阱表示,在这种情况下,行为是未定义的.
int logicalrightshift_v6(int x, int n)
{
unsigned y;
memcpy (&y, &x, sizeof (x));
y >>= n;
memcpy (&x, &y, sizeof (x));
return x;
}
Run Code Online (Sandbox Code Playgroud)
适用于v5的相同注释适用于此.
另外,我认为v2,v2a,v3,v4中的(unsigned)转换是多余的.它是否正确?
这取决于.作为十六进制常量,如果该值在范围内,0x80000000则将具有类型; 否则,如果该值在范围内; 否则,如果该值在范围内; 否则(因为该值在允许的最小范围内).intintunsignedunsignedlonglongunsigned longunsigned long
如果您希望确保它具有无符号类型,则使用a U,to 将常量后缀为0x80000000U.
摘要:
转换的数大于INT_MAX给int给出一个实现定义结果(或实际上,允许凸起实现定义的信号).
unsigned通过重复加法或减法来转换超出范围的数字UINT_MAX + 1,这意味着它取决于数学值,而不是表示.
检查否定int表示unsigned是不可移植的(但正面int表示是可以的).
通过使用按位运算符生成负零并尝试使用结果值是不可移植的.
如果您想要"逻辑转换",那么您应该在任何地方使用无符号类型.签名类型旨在处理价值重要的算法,而不是表示.