将有符号转换为无符号,反之亦然,同时扩大字节数
Casting signed to unsigned and vise versa while widening the byte count
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
,而不管目标值是有符号还是无符号。
以上说法正确吗?
正确,是源操作数决定了这一点。
uint32_t a = -1;
int64_t b = (int64_t) a;
这里没有符号扩展,因为源值是 unsigned uint32_t
。符号扩展的基本思想是保证更宽的变量具有相同的值(包括符号)。来自无符号整数类型,该值始终为正数。这包含在下面的标准片段 /1
中。
负号扩展(在二进制补码值中的最高 1 位被复制到更宽类型中的所有更高位的意义上)只发生 (a)当 signed 类型的宽度被扩展时,因为只有有符号类型可以为负数。
If the original 32 bit number is signed and negative, casting it to a 64 bit value will add 1's to its left regardless of the destination value being signed or unsigned.
下面的标准片段 /2
涵盖了这一点。在扩展位时您仍然必须保持值的符号但是将负值(假设源为负)推入无符号变量将简单地数学添加MAX_VAL + 1
到值直到它在目标类型的范围内(实际上,对于二进制补码,没有进行添加,它只是以不同的方式解释相同的位模式)。
标准中涵盖了这两种情况,在本例中为 C11 6.3.1.3 Signed and unsigned integers /1
和 /2
:
1/ When a value with integer type is converted to another integer type other than _Bool
, if the value can be represented by the new type, it is unchanged.
2/ Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
3/ Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
请注意,上面的前两点涵盖了您的扩大转化。我已经包括了第三个完成点,因为它涵盖了从 uint32_t
到 int32_t
或 unsigned int
到 long
的转换,它们具有相同的宽度(它们都有最小 range 但不要求 unsigned int
比 long
).
(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)
了解更多详情。
无论如何,固定宽度类型 是 二进制补码,因此您不必为示例代码担心这方面的问题。
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
,而不管目标值是有符号还是无符号。
以上说法正确吗?
正确,是源操作数决定了这一点。
uint32_t a = -1;
int64_t b = (int64_t) a;
这里没有符号扩展,因为源值是 unsigned uint32_t
。符号扩展的基本思想是保证更宽的变量具有相同的值(包括符号)。来自无符号整数类型,该值始终为正数。这包含在下面的标准片段 /1
中。
负号扩展(在二进制补码值中的最高 1 位被复制到更宽类型中的所有更高位的意义上)只发生 (a)当 signed 类型的宽度被扩展时,因为只有有符号类型可以为负数。
If the original 32 bit number is signed and negative, casting it to a 64 bit value will add 1's to its left regardless of the destination value being signed or unsigned.
下面的标准片段 /2
涵盖了这一点。在扩展位时您仍然必须保持值的符号但是将负值(假设源为负)推入无符号变量将简单地数学添加MAX_VAL + 1
到值直到它在目标类型的范围内(实际上,对于二进制补码,没有进行添加,它只是以不同的方式解释相同的位模式)。
标准中涵盖了这两种情况,在本例中为 C11 6.3.1.3 Signed and unsigned integers /1
和 /2
:
1/ When a value with integer type is converted to another integer type other than
_Bool
, if the value can be represented by the new type, it is unchanged.2/ Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
3/ Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
请注意,上面的前两点涵盖了您的扩大转化。我已经包括了第三个完成点,因为它涵盖了从 uint32_t
到 int32_t
或 unsigned int
到 long
的转换,它们具有相同的宽度(它们都有最小 range 但不要求 unsigned int
比 long
).
(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)
了解更多详情。
无论如何,固定宽度类型 是 二进制补码,因此您不必为示例代码担心这方面的问题。