繁体   English   中英

C++ 通常的算术转换不转换

[英]C++ usual arithmetic conversions not converting

首先,我想指出的是,我确实阅读了一些标题大致相似的其他答案。 然而,那些要么指的是我认为是标准的旧版本,要么处理促销,而不是转换。

我一直在浏览“C++速成课程”,作者在探索内置运算符的章节中陈述了以下内容:

如果没有任何浮点提升规则适用,则检查任一参数是否已签名。 如果是这样,两个操作数都被签名。 最后,与浮点类型的提升规则一样,最大操作数的大小用于提升另一个操作数:...

如果我正确阅读了标准,这不是真的,因为根据 cppreference.com,

如果两个操作数都是有符号的或都是无符号的,则转换等级较小的操作数将转换为整数转换等级较大的操作数

否则,如果无符号操作数的转换等级大于或等于有符号操作数的转换等级,则将有符号操作数转换为无符号操作数的类型。

否则,如果有符号操作数的类型可以表示无符号操作数的所有值,则将无符号操作数转换为有符号操作数的类型

否则,两个操作数都被转换为有符号操作数类型的无符号对应物。

更让我困惑的是以下代码:

printf("Size of int is %d bytes\n", sizeof(int));
printf("Size of short is %d bytes\n", sizeof(short));
printf("Size of long is %d bytes\n", sizeof(long));
printf("Size of long long is %d bytes\n", sizeof(long long));
unsigned int x = 4000000000;
signed int y = -1;
signed long z = x + y;
printf("x + y = %ld\n", z);

产生以下输出:

Size of int is 4 bytes
Size of short is 2 bytes
Size of long is 8 bytes
Size of long long is 8 bytes
x + y = 3999999999

据我了解标准, y应该已转换为unsigned int ,从而导致不正确的结果。 结果正确的,这使我假设在这种情况下不会发生转换。 为什么? 我将不胜感激对此问题的任何澄清。 谢谢你。

(我也很感激有人告诉我,我在现实生活中永远不需要这种神秘的知识,即使它不是真的 - 只是为了安心。)

负有符号值到无符号值的转换导致对应于它的二进制补码值。 添加这个无符号值将导致溢出,然后导致与添加负值完全相同的值。

顺便说一句:这就是处理器如何进行减法的技巧(或者在现代我错了吗?)。

如果我正确阅读了标准,这不是真的

你是对的。 CCC是错误的。

据我了解标准, y 应该已转换为 unsigned int,

它做了。

结果是正确的,这使我假设在这种情况下不会发生转换。

你假设错了。

因为-1不是表示的无符号数,它只是转换为表示数表示的是全等- 1模表示的值的数量。 当您将其添加到 x 时,结果大于任何可表示的值,因此结果再次是与模全等的可表示值。 由于模算术工作的神奇之处,您会得到正确的结果。

您添加了一个无符号整数和一个有符号整数,因此两个操作数的转换等级相同。 所以第二条规则将适用(强调我的):

[否则,]如果无符号操作数的转换等级大于或等于有符号操作数的转换等级,则将有符号操作数转换为无符号操作数的类型。

  • -1 被转换为无符号类型,方法是将最大的 unsigned int 的 2 的最小幂加到它上面(实际上它以 2 的补码表示)
  • 该数字与 4000000000 相加,结果是总和的模数和 2 的幂(实际上丢弃其较高位)
  • 然后你就得到了预期的结果。

标准要求转换为无符号类型,因为无符号溢出是由标准定义的,而无符号溢出则不是。

从代码片段的第一个打印语句的输出中可以明显看出,由于 int 的大小是 4 个字节,因此 unsigned int 值的范围可以从 0 到 4,294,967,295。 (2^32-1)

因此,以下声明完全有效:

unsigned int x = 4000000000;

请记住,将 x 声明为无符号整数会使其自身是有符号对应物的 (+ve) 范围的两倍,但它落在完全相同的范围内,因此它们之间的转换是可能的。

signed int y = -1;
signed long z = x + y;
printf("x + y = %ld\n", z);

因此,对于上面的其余代码,y 和 z 的操作数是有符号 (+ve/-ve) 还是无符号 (+ve) 并不重要,因为您首先处理的是无符号整数,通过将UINTMAX+1添加到有符号版本,将有符号整数转换为其相应的无符号整数版本。 这里UINTMAX代表最大的无符号值,它与 2 的最小幂相加 (2^0=1)。 最终结果显然会大于我们可以存储的值,因此它被视为模数来收集正确的 unsigned int。

但是,如果您要将 x 转换为有符号:

signed int x = 4000000000;
int y = -1;
long z = x + y;
printf("x + y = %ld\n", z);

你会得到你所期望的:

x + y = -294967297

我也很感激有人告诉我在现实生活中我永远不需要这种神秘的知识

上面没有提到的一个隐式提升是错误的常见原因:

unsigned short x = 0xffff;
unsigned short y = 0x0001;
if ((x + y) == 0)

这样做的结果是错误的,因为算术运算至少被隐式提升为 int。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM