首页 > 解决方案 > 将有符号转换为无符号,反之亦然,同时扩大字节数

问题描述

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重要。即,如果原始的 32 位数字是无符号的,则将其转换为 64 位值将添加0's到其左侧,而不管目标值是有符号还是无符号,并且;

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

上述说法正确吗?

标签: csign-extension

解决方案


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

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

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

负号扩展(从某种意义上说,二进制补码值的最高 1 位被复制到更宽类型(a)中的所有高位)仅在有符号类型的宽度扩展时发生,因为只有有符号类型可以消极的。


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

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


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

1/ 当一个整数类型的值转换为除 以外的其他整数类型_Bool时,如果该值可以用新的类型表示,则保持不变。

2/ 否则,如果新类型是无符号的,则在新类型可以表示的最大值的基础上重复加减1,直到值在新类型的范围内。

3/ 否则,新类型是有符号的,值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。

请注意,上述前两点涵盖了您的扩大转换。我已经包含了完成的第三点,因为它涵盖了从uint32_t到 的转换int32_t,或者unsigned intlong它们具有相同宽度的位置(它们都有最小范围,但没有要求unsigned int比 更“薄” long)。


(a)这可能在一个的补码或符号大小表示中有所不同,但由于它们正在被删除,所以没有人真正关心那么多。

看:

了解更多详情。

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


推荐阅读