所有这些功能都在我的机器上给出了预期的结果。他们都在其他平台上工作吗?
更具体地说,如果 x 在 1 的补码机器上具有位表示 0xffffffff 或在有符号幅度机器上具有 0x80000000 ,那么标准对 (unsigned)x 的表示有何规定?
另外,我认为 v2、v2a、v3、v4 中的(无符号)转换是多余的。这样对吗?
假设 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;
}
最佳答案
If x has the bit representation 0xffffffff on 1's complement machines or 0x80000000 on signed magnitude machines what does the standard says about the representation of (unsigned)x ?
转换为
unsigned
是根据值而不是表示来指定的。如果您转换 -1
至 unsigned
, 你总是得到 UINT_MAX
(所以如果你的 unsigned
是 32 位,你总是得到 4294967295
)。无论您的实现使用何种有符号数的表示法,都会发生这种情况。同样,如果您转换
-0
至 unsigned
那么你总是得到0
. -0
在数值上等于 0。请注意,支持负零不需要一个补码或符号大小实现;如果没有,那么访问这样的表示会导致程序具有未定义的行为。
一一浏览你的函数:
int logicalrightshift_v1(int x, int n)
{
return (unsigned)x >> n;
}
此函数对
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);
}
几乎所有关于此的内容都可以是实现定义的。假设您试图在
msb
中创建一个值如果符号位为 1,值位为 0,则无法通过使用移位来便携地执行此操作 - 您可以使用 ~INT_MAX
,但是这允许在不允许负零的符号幅度机器上具有未定义的行为,并且允许在二进制补码机器上给出实现定义的结果。0x7fffffff
的种类和 0x80000000
将取决于各种类型的范围,这将影响此表达式中其他值的提升方式。int logicalrightshift_v2a(int x, int n)
{
return ((x & 0x7fffffff) >> n) | (x & (unsigned)0x80000000 ? (unsigned)0x80000000 >> n : 0);
}
如果您创建一个
unsigned
不在 int
范围内的值(例如,给定 32 位 int
, values > 0x7fffffff
)然后 return 语句中的隐式转换产生一个实现定义的值。这同样适用于 v3 和 v4。int logicalrightshift_v5(int x, int n)
{
unsigned y;
*(int *)&y = x;
y >>= n;
*(unsigned *)&x = y;
return x;
}
这仍然是实现定义的,因为未指定
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;
}
适用于 v5 的相同评论也适用于此。
Also, I think the (unsigned) cast in v2, v2a, v3, v4 is redundant. Is this correct?
这取决于。作为十六进制常量,
0x80000000
将有类型 int
如果该值在 int
的范围内;否则 unsigned
如果该值在 unsigned
的范围内;否则 long
如果该值在 long
的范围内;否则 unsigned long
(因为该值在 unsigned long
的最小允许范围内)。如果你想确保它是无符号类型,那么在常量后面加上
U
, 至 0x80000000U
.摘要:
INT_MAX
的数字至 int
给出实现定义的结果(或者实际上,允许引发实现定义的信号)。 unsigned
通过重复加减 UINT_MAX + 1
来完成,这意味着它取决于数学值,而不是表示。 int
表示为 unsigned
不可移植(不过,正 int
表示是可以的)。 如果您想要“逻辑移位”,那么您应该到处使用无符号类型。有符号类型是为处理算法而设计的,其中值是重要的,而不是表示。
关于负零的 C 标准(1 的补码和有符号幅度),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7925598/