C++ 无符号和有符号的转换
C++ unsigned and signed conversion
我以前见过此类问题,但提供的答案并不能为我解决所有问题。发布此问题时,通常会附有下一个示例:
#include <iostream>
int main()
{
unsigned int u = 10;
int i = -42;
std::cout << i + i << std::endl;
std::cout << i + u << std::endl;
return 0;
}
输出:
-84
4294967264
所有工作都按预期将 int 转换为 unsigned。但是如果 i
的绝对值小于 u
似乎没有这样的转换发生。
#include <iostream>
int main()
{
unsigned int u = 10;
int i = -3;
std::cout << i + i << std::endl;
std::cout << i + u << std::endl;
return 0;
}
输出:
-6
7
我没有看到任何提到它的答案,也找不到任何解释。尽管这似乎是合乎逻辑的事情,但我对此没有任何解释。
But if absolute value of i is less than u it seems like no such conversion is hapenning.
您的假设是错误的:这种转换正在发生。而“这种转换”,我的意思是当-3转换为无符号类型时,结果是4'294'967'293。
I haven't seen any answer mentioning it and can't find any explanation.
无符号算术是模块化的。这就是模块化算法的工作原理。
要理解模运算,请思考 12 小时制的工作原理。它也是模块化的。您会注意到钟面没有任何负数:
- 时间是 10 点(今天上午)。 3小时前,现在几点了?那是早上 7 点(今天)。
- 时间是 10 点(今天上午)。 42小时前,现在几点了?下午4点(前天)
无符号算术的工作原理与此完全相同。除了,在 32 位类型的情况下,有 4'294'967'296 个值,而不是 12 个值。
为了将不可表示的值转换为可表示的范围,只需加上或减去模数(时钟为 12,32 位无符号整数为 4'294'967'296)直到值在可表示范围。
这是时钟示例的数学运算:
R ≡ 10 + (-3) (mod 12)
// -3 = 9 + (12 * -1)
R ≡ 10 + 9 (mod 12)
R ≡ 19 (mod 12)
// 19 = 7 + (12 * 1)
R ≡ 7 (mod 12)
R ≡ 10 + (-42) (mod 12)
// -42 = 6 + (12 * -4)
R ≡ 10 + 6 (mod 12)
R ≡ 16 (mod 12)
// 16 = 4 + (12 * 1)
R ≡ 4 (mod 12)
以下是您示例的数学公式:
R ≡ 10 + (-42) (mod 4'294'967'296)
// -42 = 4'294'967'254 + (4'294'967'296 * -1)
R ≡ 10 + 4'294'967'254 (mod 4'294'967'296)
R ≡ 4'294'967'264 (mod 4'294'967'296)
R ≡ 10 + (-3) (mod 4'294'967'296)
// -3 = 4'294'967'293 + (4'294'967'296 * -1)
R ≡ 10 + 4'294'967'293 (mod 4'294'967'296)
R ≡ 4'294'967'303 (mod 4'294'967'296)
// 4'294'967'303 = 7 + (4'294'967'296 * -1)
R ≡ 7 (mod 4'294'967'296)
之后:
unsigned int u = 10;
int i = -3;
i + u
的评估首先将 i
转换为 unsigned int
。对于 32 位 unsigned int
,此转换换行模 232,即 4,294,967,296。这种环绕的结果是 −3 + 4,294,967,296 = 4,294,967,293。
转换后,我们添加 4,294,967,293(转换后 i
)和 10(u
)。这将是 4,294,967,303。由于这超出了 32 位 unsigned int
的范围,因此它以 4,294,967,296 为模进行换行。结果是 4,294,967,303 − 4,294,967,296 = 7.
这样就打印出了“7”
有两个概念对于理解负数和无符号数的关系非常重要。第一个是 Ones compliment。这是一个非常古老的标准,不再使用,但它很容易理解。负数只是每一位的非。 -1 = ~1 = 1111...1110。
补码的问题是有两个零 (+0/-0)。这很快就会变得复杂。考虑
X = 2-2
Y = -2+2
X != Y
这就是现代计算机和 C/C++ 采用 twos compliment 的原因。 Twos compliment drops -0(没有这样的东西)所以
-1 = 1111...1111
-2 = 1111...1110
so -v = ~1+1
您看到没有在有符号和无符号之间进行转换的“操作”。在硬件中,每个只是一个位向量。该值是有符号的还是无符号的,只是如何解释这些位的问题。要证明这一点,您只需尝试
printf("%x", -1); // print a signed value as if it was unsigned
所以回到你的问题,任何小的(+v)数字都是一样的,无论是否签名。 (signed) 1 与 unsigned (1) 相同。只有当最高有效位为 1 时,解释才会改变。
顺便说一句:在 C/C++ 中,“unsigned int”可以缩写为“unsigned”,这是任何未指定的类型都假定为 int 的规则的残余。
我以前见过此类问题,但提供的答案并不能为我解决所有问题。发布此问题时,通常会附有下一个示例:
#include <iostream>
int main()
{
unsigned int u = 10;
int i = -42;
std::cout << i + i << std::endl;
std::cout << i + u << std::endl;
return 0;
}
输出:
-84
4294967264
所有工作都按预期将 int 转换为 unsigned。但是如果 i
的绝对值小于 u
似乎没有这样的转换发生。
#include <iostream>
int main()
{
unsigned int u = 10;
int i = -3;
std::cout << i + i << std::endl;
std::cout << i + u << std::endl;
return 0;
}
输出:
-6
7
我没有看到任何提到它的答案,也找不到任何解释。尽管这似乎是合乎逻辑的事情,但我对此没有任何解释。
But if absolute value of i is less than u it seems like no such conversion is hapenning.
您的假设是错误的:这种转换正在发生。而“这种转换”,我的意思是当-3转换为无符号类型时,结果是4'294'967'293。
I haven't seen any answer mentioning it and can't find any explanation.
无符号算术是模块化的。这就是模块化算法的工作原理。
要理解模运算,请思考 12 小时制的工作原理。它也是模块化的。您会注意到钟面没有任何负数:
- 时间是 10 点(今天上午)。 3小时前,现在几点了?那是早上 7 点(今天)。
- 时间是 10 点(今天上午)。 42小时前,现在几点了?下午4点(前天)
无符号算术的工作原理与此完全相同。除了,在 32 位类型的情况下,有 4'294'967'296 个值,而不是 12 个值。
为了将不可表示的值转换为可表示的范围,只需加上或减去模数(时钟为 12,32 位无符号整数为 4'294'967'296)直到值在可表示范围。
这是时钟示例的数学运算:
R ≡ 10 + (-3) (mod 12)
// -3 = 9 + (12 * -1)
R ≡ 10 + 9 (mod 12)
R ≡ 19 (mod 12)
// 19 = 7 + (12 * 1)
R ≡ 7 (mod 12)
R ≡ 10 + (-42) (mod 12)
// -42 = 6 + (12 * -4)
R ≡ 10 + 6 (mod 12)
R ≡ 16 (mod 12)
// 16 = 4 + (12 * 1)
R ≡ 4 (mod 12)
以下是您示例的数学公式:
R ≡ 10 + (-42) (mod 4'294'967'296)
// -42 = 4'294'967'254 + (4'294'967'296 * -1)
R ≡ 10 + 4'294'967'254 (mod 4'294'967'296)
R ≡ 4'294'967'264 (mod 4'294'967'296)
R ≡ 10 + (-3) (mod 4'294'967'296)
// -3 = 4'294'967'293 + (4'294'967'296 * -1)
R ≡ 10 + 4'294'967'293 (mod 4'294'967'296)
R ≡ 4'294'967'303 (mod 4'294'967'296)
// 4'294'967'303 = 7 + (4'294'967'296 * -1)
R ≡ 7 (mod 4'294'967'296)
之后:
unsigned int u = 10;
int i = -3;
i + u
的评估首先将 i
转换为 unsigned int
。对于 32 位 unsigned int
,此转换换行模 232,即 4,294,967,296。这种环绕的结果是 −3 + 4,294,967,296 = 4,294,967,293。
转换后,我们添加 4,294,967,293(转换后 i
)和 10(u
)。这将是 4,294,967,303。由于这超出了 32 位 unsigned int
的范围,因此它以 4,294,967,296 为模进行换行。结果是 4,294,967,303 − 4,294,967,296 = 7.
这样就打印出了“7”
有两个概念对于理解负数和无符号数的关系非常重要。第一个是 Ones compliment。这是一个非常古老的标准,不再使用,但它很容易理解。负数只是每一位的非。 -1 = ~1 = 1111...1110。 补码的问题是有两个零 (+0/-0)。这很快就会变得复杂。考虑
X = 2-2 Y = -2+2 X != Y
这就是现代计算机和 C/C++ 采用 twos compliment 的原因。 Twos compliment drops -0(没有这样的东西)所以
-1 = 1111...1111 -2 = 1111...1110 so -v = ~1+1
您看到没有在有符号和无符号之间进行转换的“操作”。在硬件中,每个只是一个位向量。该值是有符号的还是无符号的,只是如何解释这些位的问题。要证明这一点,您只需尝试
printf("%x", -1); // print a signed value as if it was unsigned
所以回到你的问题,任何小的(+v)数字都是一样的,无论是否签名。 (signed) 1 与 unsigned (1) 相同。只有当最高有效位为 1 时,解释才会改变。
顺便说一句:在 C/C++ 中,“unsigned int”可以缩写为“unsigned”,这是任何未指定的类型都假定为 int 的规则的残余。