【问题标题】:What is happening with signed/unsigned int conversion?有符号/无符号 int 转换发生了什么?
【发布时间】:2014-04-14 21:15:31
【问题描述】:

只是仔细检查。在一些教程中,我发现了这样的代码:

#include <iostream>
using namespace std;

/* This program shows the difference between
 * signed and unsigned integers.
*/
int main()
{
   short int i;           // a signed short integer
   short unsigned int j;  // an unsigned short integer

   j = 50000;

   i = j;
   cout << i << " " << j;

   return 0;
}

输出:-15536​​ 50000

然后它解释了输出:“上面的结果是因为将50,000表示为短无符号整数的位模式被短解释为-15,536。”

我认为这是一个错误的解释——还是英文问题? 我认为输出负值的原因是 50000 不适合 2 字节有符号整数,我错了吗?

【问题讨论】:

  • 标志有点。 50k 使用无符号的所有 16 位。当它转换为有符号时,高位是符号,其他 15 位是 15,536。
  • “a short”是“2 字节有符号整数”。
  • 不遵循答案。如果我将 30000 分配给 i,它会显示 30000-但由于 50000 不适合带符号的短,因此不是负值吗? (与网站上的解释相反)
  • 查看对我的评论的编辑。 30k 适合 15 位。
  • 我看不出你引用的内容在哪里暗示了这一点。 16 位就是 16 位。如果设置了高位,即当数字超过 32768 时,则数字将是负数,如果它是有符号的。它说 50k 的位模式被解释为 -15,536 就是这种情况。位没有改变;只有他们的解释。

标签: c++


【解决方案1】:

解释中的推理对于特定平台可能是准确的,但在我看来,它与最终打印的内容无关。更准确的说法是

“上述结果是因为从实现定义的 unsigned short int 值 50000 到 signed short int 的转换返回的位模式导致值 -15,536。”

毫不奇怪,将所述值发送到std::cout 将为所述值的signed short int 结果产生正确的输出。更改(转换)的来源很重要,在这种情况下,它是实现定义的。他们的措辞很弱,并且可以通过将 any 值分配给 any 整数值来应用 相同 语句,因此实际上他们的解释最终是 毫无意义。


不想浪费前面的答案(在我更好地理解问题之前),享受一些轻松的阅读。要确定了解为什么会发生这种情况,您必须查阅实施文档以了解这种性质的转换。不是您可能想听到的答案,但背后是有原因的。

这是因为 价值提升 通过整数转换 rank。您收到的值实际上是依赖于实现的,标准中涵盖了具体原因。

我将省略最基本的东西,直接进入它的实质:

C++11 §4.7 积分提升 [conv.integral]

  1. 整数类型的纯右值可以转换为另一种整数类型的纯右值。无作用域枚举类型的纯右值可以转换为整数类型的纯右值。

  2. 如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模 2n,其中 n 是用于表示无符号类型的位数)。 [ 注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。 ——尾注]

  3. 如果目标类型是有符号的,如果它可以在目标类型(和位域宽度)中表示,则值不变;否则,该值是实现定义的。

  4. 如果目标类型是 bool,请参见 4.12。如果源类型为 bool,则值 false 转换为 0,值 true 转换为 1。

  5. 允许作为积分促销的转换不包括在积分转换集中。

值 50000 无法在您的平台上以 signed short int 表示。因此,结果是实现定义的。从外观上看,您的实现只是将字节从一个存储到另一个,结果是符号位(也包含在标准中,但为简洁起见)被点亮并且报告的数字为负数。但请记住,这是由实现定义的,您不能在所有平台上都依赖此结果

【讨论】:

  • 而且...我刚刚意识到这是作为 C++ 发布的,所以我放弃了这个,直到/如果/当我有时间在 C++ 中找到相关的标准部分。对此感到抱歉。
  • @WhosCraig:谢谢——但这不是我问的。我不是在寻找为什么我得到负值的原因,我也认为这将是实现定义的——我只是对教程给出的打印负值的原因有问题(见问题)。不过还是谢谢你的解释!!
  • @dmcr_code 好的。我想我现在明白了。感谢您的澄清
  • 我主要担心的是人们可能会认为 - 从教程的解释中 - 任何转换为​​ int 的无符号值都会被解释并打印为负数,不是吗?(​​如果 j 适合短 int , 数字将按原样打印)
  • 好的,我会接受这个,因为我们承认对教程的解释是不正确的...... ps。我将阅读更多关于签名/未签名 int 促销等的信息,以备将来使用。谢谢大家的意见。
【解决方案2】:

你的答案和书上的答案都是正确的

50000 = 0xc350

包含 0xc350 的有符号 16 位短字节被解释为 -15,536

所以它们是正确的(位模式的解释)

如果 i 是 32 位 int,那么将 0xc350 放入其中将被解释为 50,000

所以你是对的(我太小了)

【讨论】:

  • i=30000 不会打印为负数。我说 50000 打印为负数,因为它不适合 short int,我错了吗?
  • @dmcr_code 位模式适合短整数,它只是意味着不同的东西。
  • re "一个包含 0xc350 的有符号 16 位短被解释为 -15,536",这取决于有符号短是使用二进制补码形式、一个补码形式还是符号和大小。
【解决方案3】:

首先,整数类型需要用纯二进制表示,目前教程是正确的。

其次,short 至少需要 16 位。如果它更多,那么您将看不到您所做的效果或任何效果。从您的描述中不清楚本教程是否盲目假设 short 一定是 16 位(错误),或者它是否只是使用 some 具体示例,理解它取决于编译器等。

第三,转换为有符号类型... 如果值无法表示,则正式实现定义的行为。这意味着您不能保证价值的变化。相反,您可以原则上获得任何效果,例如崩溃。

[缺少其他行为的示例,因为我无法诱使 g++ 4.8.2 捕获您的示例代码,即使使用 -ftrapv]

…产生一个相同的值,如果它可以表示,或者由实现定义。

也就是说,C++ 保证 无符号算术 执行模 2n,其中 n 是数字值表示位,例如16 在你的例子中。并且使用非常常见的 二进制补码形式 表示有符号整数,负整数值 -x 表示为 -x + 2 的位模式n。因此,如果您从后一个值(将位模式解释为无符号)开始为 50 000,具有 16 个值位和二进制补码形式,您将得到有符号值 50 000 - 216 = 50 000 - 65 536 = -15 536

【讨论】:

  • 反正教程没有提到j在short int范围内,负值不会显示。
  • 我修正了“UB”的声明,因为它确实是实现定义的。
【解决方案4】:

50,000 用二进制表示为 - 1100 0011 0101 0000。

在有符号位中,最左边的位是符号。在有符号整数中,“0”表示负数(因此是 -15536​​),而在无符号整数中,这没有区别。至于为什么数字本身会改变,我不知道。

【讨论】:

  • 您的位模式正确,但 short 只有 16 位。因此,short 的 MSB 是1,这就是数字为负的原因。
  • 我没有注意到“短整数”。 C 真的不是我的菜。
【解决方案5】:

你是对的,这本书也是对的。 unsigned short 的值可以是 0 到 65535。signed short 的值可以是 -32768 到 32767。因此,0 到 32767 之间的任何值都适用于 signedunsigned

但是,像 50000 这样的数字对于 signed short 来说太大了,因此当您将 50000 分配给 signed short 时,会导致数字溢出。

【讨论】:

  • 你的最后一句话就是我的意思 - 因此为什么显示负值是正确的?只是教程中没有特别提到,我想就是这样(我说可能是英文问题)
  • 是的,我认为这主要是一个英文问题。 32768 到 65535 之间的任何值对于 signed short 来说都太大了,因此分配这些值会导致溢出,在大多数系统上最终会显示为负数。
  • 是的,作者没有说如果该值适合 i 那么该值将正确显示。
  • 没错,作者只是举了一个数字太大的例子,而不是解释数字的一般工作原理。我同意作者本可以做得更好。
【解决方案6】:

unsigned short 是一个不使用符号位的 16 位值 — 所有值都假定为正数。

二进制存储的十进制值50000是

1100 0011 0101 0000
^

最左边的位(Most Significant Bit - MSB)是1,代表2^15,即32,768

整个位模式为 32,768 + 16,384 + 512 + 256 + 64 + 16 = 50,000

当您将其转换为 signed 短时,位模式未更改,但 MSB 不再代表 32,768 — 它现在代表 符号,其余位是该值的 2 补码。在有符号值中,最高位是符号,1 是负数

30,000 不会发生这种情况,因为那是

0111 0101 0011 0000
^

当它被转换为 signed 时,最左边的0 表示符号,而 0 是 所以其余位仍按原样解释,而不是 2 的补码,因此它们仍然代表相同的值 30,000

【讨论】:

  • 请注意,short 通常可以超过 16 位。另请注意,虽然 2 的补码是当今最常见的有符号整数表示,但它并不是 C++ 标准允许的唯一表示。
  • @Cheers - 我完全理解这一点,我必须使 实际上 成为便携式的非便携式 C 的悠久历史。我没有检查官方的 C++ 标准,但this page 暗示标准是(至少)16 位,实际上通常是 16 位。我试图根据 OP 经验的实际情况尽可能清楚地解释。
  • @StepenP:很遗憾,您不会在“官方 C++ 标准”中找到尺寸信息。它来自C标准的取值范围要求。 C 标准已“合并”到 C++ 标准中。
猜你喜欢
  • 1970-01-01
  • 2013-06-23
  • 2013-01-05
  • 1970-01-01
  • 1970-01-01
  • 2012-11-18
  • 2011-10-25
  • 2016-02-17
  • 2013-12-19
相关资源
最近更新 更多