如何在保留原始位模式的同时将(int)转换为(unsigned int)?

时间:2016-11-05 16:18:02

标签: c casting bit-manipulation

假设我们定义:

short x = -1;
unsigned short y = (unsigned short) x;

根据C99标准:

  

否则,如果新类型是无符号的,则转换为   重复加或减一个以上的最大值   可以用新类型表示,直到值在范围内   新型。 (ISO / IEC 9899:1999 6.3.1.3/2)

因此,假设的两个字节和一个二进制补码表示,这两个整数的位模式是:

x = 1111 1111 1111 1111 (value of -1),
y = 1111 1111 1111 1111 (value of 65535).

由于-1不在无符号短的值范围内,并且无符号短中可以表示的最大值为65535,因此将65536添加到 - 1得到65535,其范围为无符号短。因此,在从 int 转换为无符号时,位保持不变,但代表的值已更改。

但是,该标准还表示,表示可能是两个补码,一个补码,或符号和幅度。 "其中哪些适用于实施定义,......" (ISO / IEC 9899:1999 6.2.6.2/2)

在使用补码的系统上,x在投射前会被表示为1111 1111 1111 1110,而在使用符号和幅度表示的系统上,x将表示为1000 0000 0000 0001。这两个位模式都表示值-1,它不在无符号短的值范围内,因此在每种情况下65536将被添加到-1以使值进入范围。演员表之后,这两个位模式都是1111 1111 1111 1111

因此,从 int unsigned int 的转换中保留位模式是依赖于实现的。

似乎能够在保留位模式的同时将 int 转换为 unsigned int ,这对于对负数进行位移操作似乎是一个方便的工具,并且我已经看到它主张作为一种技术。但是这种技术似乎并不能保证符合标准。

我在这里是否正确阅读了标准,或者我是否误解了从有符号类型到无符号类型的转换细节?两个补码的实现是否足够普遍,从 int unsigned 的转换中的位模式保留的假设是合理的?如果没有,是否有更好的方法来保留从 int unsigned int 的转换下的位模式?

修改

我最初的目标是找到一种方法将 int 强制转换为 unsigned int ,以便保留位模式。我认为从 int int N _t 的演员可以帮助实现这一目标:

unsigned short y = (unsigned short)(int16_t) x;

但当然这个想法是错的!最好这只会强制执行两个补码表示,然后再转换为无符号,这样最终的位模式就是两个补码。我很想删除这个问题,但我仍然有兴趣从 int 转换为 unsigned int 来保留位模式,@ Leushenko提供了一个非常整洁的使用工会解决这个问题。但是,我已经改变了问题的标题以反映最初的意图,我已经编辑了结束问题。

2 个答案:

答案 0 :(得分:5)

如果你特别想要保留比特模式高于其他任何东西,这似乎是通过联合而不是强制转换运算符的一个很好的用例:

union S2US { short from; unsigned short to; };

...
short value = ...
unsigned short bits = (union S2US){ .from = value }.to;
...

footnote 95(第6.5.2.3节)中所述,“如果用于读取union对象内容的成员与上次用于在对象中存储值的成员不同,则值的对象表示的适当部分被重新解释为新类型中的对象表示,如6.2.6“中所述。重新解释不涉及以任何方式操纵数据,因此根据成员类型,提取的值不保证与插入的值具有任何直接的算术关系,但保证具有完全相同的内存表示。

由于整数类型的有符号和无符号版本的大小相同(6.2.5 p6),并且union的所有成员必须在同一位置(6.7.2.1 p16)开始存储,只包含相同宽度的有符号和无符号整数的联合必须在任一方向上忠实地将所有位从一个复制到另一个。

答案 1 :(得分:1)

  

(int)转换为(unsigned)(intN_t)以保留位模式是否可以接受?

通常,是的,但未指定在C中为所有值执行此操作。 C尝试在类型转换期间维护值,而不是位模式。

如果(例如x)可以表示为intunsigned和所选的intN_t,那么该值的位模式是没变。所以问题涉及x何时为负面。

C指定转换为任何无符号类型,通过加/减“无符号类型的最大值+ 1”来处理溢出,直到结果在范围内。如果带符号类型使用2的补码编码,则低有效位的模式将与目标无符号类型匹配。

在实现定义的行为中转换为有符号整数类型 - 因此OP的困境。

C仅指定有符号整数类型的范围及其对应的无符号整数类型都需要将愤怒:0编码为有符号类型最大值。 IOW INT_MAX == UINT_MAX是允许的。在这种罕见的平台上,从int转换为unsignedint会失去信号。

如果代码需要保留某些已签名类型的位模式,则union数组unsigned char适用于所有情况。

union uA {
  some_signed_int_type i;
  unsigned char uc[sizeof (some_signed_int_type)];
}

具有固定宽度无符号类型的联合(这些类型是可选),其最大值大于有符号最大值,用于维护位模式。即使是正值,也不要依赖于相同的值。固定宽度类型没有填充,而不是常规签名类型。

assert(uintN_MAX > some_signed_type_max);
union uB {
  some_signed_int_type i;
  uintN_t u;
}

unsigned char(u)intN_t的主要好处是指定这些类型没有填充。

相关问题