c-在扩大字节计数的同时,将有符号转换为无符号,反之亦然


uint32_t a = -1;            // 11111111111111111111111111111111
int64_t b = (int64_t) a;    // 0000000000000000000000000000000011111111111111111111111111111111
int32_t c = -1;             // 11111111111111111111111111111111
int64_t d = (int64_t) c;    // 1111111111111111111111111111111111111111111111111111111111111111

从上面的观察来看,似乎只有原始值的sign才重要。I.e如果原始32位数字是无符号的,则将其强制转换为64位值将在其左侧添加0's,而不管目标值是有符号的还是无符号的;

如果原始的32位数字是有符号的且为负数,则将其强制转换为64位值将在其左侧添加1's,而不管目标值是有符号还是无符号的。

上述说法正确吗?

正确,是源操作数决定了这一点。

uint32_t a = -1;
int64_t b = (int64_t) a;

这里没有符号扩展,因为源值是一个无符号uint32_t。符号扩展的基本思想是确保更宽的变量具有相同的值(包括符号(。来自无符号整数类型的值始终为正。下面的标准片段/1涵盖了这一点。

负符号扩展(在2的补码值中的前1位被复制到较宽类型(a(中的所有高位的意义上(只有当有符号类型在宽度上扩展时才会发生,因为只有有符号类型可以是负的。


如果原始32位数字是有符号的且为负数,则将其强制转换为64位值将在其左侧添加1,而不管目标值是有符号还是无符号的。

下面的标准片段/2涵盖了这一点。当扩展位时,您仍然必须保持值的符号,但将负值(假设源为负数(推入无符号变量将简单地在数学上将MAX_VAL + 1添加到值中,直到它在目标类型的范围内(实际上,对于2的补码,不进行加法,它只是以不同的方式解释相同的位模式(。


这两种情况都包含在标准中,在本例中为C11 6.3.1.3 Signed and unsigned integers /1/2:

1/当一个具有整数类型的值转换为_Bool以外的另一个整数类型时,如果该值可以用新类型表示,则该值不变。

2/否则,如果新类型是无符号的,则通过重复地将新类型中可以表示的最大值多加或减去一来转换该值,直到该值在新类型的范围内为止。

3/否则,新类型被签名,并且不能在其中表示值;要么结果是实现定义的,要么产生实现定义的信号。

请注意,前面两点涵盖了您的加宽转换。我已经包含了完成的第三点,因为它涵盖了从uint32_tint32_t的转换,或从unsigned intlong的转换,其中它们具有相同的宽度(它们都有最小的范围,但不要求unsigned intlong"薄"(。


(a(这可能在一个人的补码或符号幅度表示中有所不同,但由于它们正在被删除,没有人真的那么在乎。

参见:

  • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r1.html(WG21,C++(;以及
  • http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2218.htm(WG14,C(

了解更多详细信息。

在任何情况下,固定宽度类型都是二的补码,因此您不必担心示例代码的这一方面。

最新更新