简体   繁体   English

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

[英]C++ usual arithmetic conversions not converting

First, I'd like to point out that I did read some of the other answers with generally similar titles.首先,我想指出的是,我确实阅读了一些标题大致相似的其他答案。 However, those either refer to what I assume is an older version of the standard, or deal with promotions , not conversions.然而,那些要么指的是我认为是标准的旧版本,要么处理促销,而不是转换。

I've been crawling through "C++ Crash Course", where the author states the following in the chapter exploring built-in operators:我一直在浏览“C++速成课程”,作者在探索内置运算符的章节中陈述了以下内容:

If none of the floating-point promotion rules apply, you then check whether either argument is signed.如果没有任何浮点提升规则适用,则检查任一参数是否已签名。 If so, both operands become signed.如果是这样,两个操作数都被签名。 Finally, as with the promotion rules for floating-point types, the size of the largest operand is used to promote the other operand: ...最后,与浮点类型的提升规则一样,最大操作数的大小用于提升另一个操作数:...

If I read the standard correctly, this is not true, because, as per cppreference.com,如果我正确阅读了标准,这不是真的,因为根据 cppreference.com,

If both operands are signed or both are unsigned, the operand with lesser conversion rank is converted to the operand with the greater integer conversion rank如果两个操作数都是有符号的或都是无符号的,则转换等级较小的操作数将转换为整数转换等级较大的操作数

Otherwise, if the unsigned operand's conversion rank is greater or equal to the conversion rank of the signed operand, the signed operand is converted to the unsigned operand's type.否则,如果无符号操作数的转换等级大于或等于有符号操作数的转换等级,则将有符号操作数转换为无符号操作数的类型。

Otherwise, if the signed operand's type can represent all values of the unsigned operand, the unsigned operand is converted to the signed operand's type否则,如果有符号操作数的类型可以表示无符号操作数的所有值,则将无符号操作数转换为有符号操作数的类型

Otherwise, both operands are converted to the unsigned counterpart of the signed operand's type.否则,两个操作数都被转换为有符号操作数类型的无符号对应物。

What confuses me even more is the fact that the following code:更让我困惑的是以下代码:

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);

produces the following output:产生以下输出:

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

As I understand the standard, y should have been converted to unsigned int , leading to an incorrect result.据我了解标准, y应该已转换为unsigned int ,从而导致不正确的结果。 The result is correct, which leads me to assume that no conversion happens in this case.结果正确的,这使我假设在这种情况下不会发生转换。 Why?为什么? I would be grateful for any clafirication on this matter.我将不胜感激对此问题的任何澄清。 Thank you.谢谢你。

(I would also appreciate someone telling me that I won't ever need this kind of arcane knowledge in real life, even if it's not true - just for the peace of mind.) (我也很感激有人告诉我,我在现实生活中永远不需要这种神秘的知识,即使它不是真的 - 只是为了安心。)

A conversion of a negative signed value to an unsigned leads into the binary complement value which corresponds to it.负有符号值到无符号值的转换导致对应于它的二进制补码值。 An addition of this unsigned value will lead into an overflow which then results just exactly in the same value as adding the negative value would do.添加这个无符号值将导致溢出,然后导致与添加负值完全相同的值。

By the way: That's the trick how the processor does a subtraction (or am I wrong in these modern times?).顺便说一句:这就是处理器如何进行减法的技巧(或者在现代我错了吗?)。

If I read the standard correctly, this is not true如果我正确阅读了标准,这不是真的

You're correct.你是对的。 CCC is wrong. CCC是错误的。

As I understand the standard, y should have been converted to unsigned int,据我了解标准, y 应该已转换为 unsigned int,

It did.它做了。

The result is correct, which leads me to assume that no conversion happens in this case.结果是正确的,这使我假设在这种情况下不会发生转换。

You assume wrong.你假设错了。

Since -1 is not representable unsigned number, it simply converts to the representable number that is representable and is congruent with - 1 modulo the number of representable values.因为-1不是表示的无符号数,它只是转换为表示数表示的是全等- 1模表示的值的数量。 When you add this to x, the result is larger than any representable value, so again result is the representable value that is congruent with the modulo.当您将其添加到 x 时,结果大于任何可表示的值,因此结果再次是与模全等的可表示值。 Because of the magic of how modular arithmetic works, you get the correct result.由于模算术工作的神奇之处,您会得到正确的结果。

You have an addition of an unsigned int and a signed int, so same conversion rank for both operands.您添加了一个无符号整数和一个有符号整数,因此两个操作数的转换等级相同。 So the second rule will apply (emphasize mine):所以第二条规则将适用(强调我的):

[Otherwise,] if the unsigned operand's conversion rank is greater or equal to the conversion rank of the signed operand, the signed operand is converted to the unsigned operand's type. [否则,]如果无符号操作数的转换等级大于或等于有符号操作数的转换等级,则将有符号操作数转换为无符号操作数的类型。

  • -1 is converted to an unsigned type by adding to it the smallest power of two greater that the highest unsigned int (in fact its representation in 2's complement) -1 被转换为无符号类型,方法是将最大的 unsigned int 的 2 的最小幂加到它上面(实际上它以 2 的补码表示)
  • that number is added to 4000000000 and the result is the modulo of the sum and that power of two (in fact discarding its higher bit)该数字与 4000000000 相加,结果是总和的模数和 2 的幂(实际上丢弃其较高位)
  • and you just get the expected result.然后你就得到了预期的结果。

The standard mandates conversion to the unsigned type because unsigned overflow is defined by the standard, while unsigned one is not.标准要求转换为无符号类型,因为无符号溢出是由标准定义的,而无符号溢出则不是。

From the output of the first print statement from your code snippet it is evident that since the size of int is 4 bytes, the unsigned int values can range from 0 to 4,294,967,295.从代码片段的第一个打印语句的输出中可以明显看出,由于 int 的大小是 4 个字节,因此 unsigned int 值的范围可以从 0 到 4,294,967,295。 (2^32-1) (2^32-1)

The declaration below is hence perfectly valid:因此,以下声明完全有效:

unsigned int x = 4000000000;

Remember here that declaring x as an unsigned integer gives itself twice the (+ve) range of a signed counterpart, but it falls in exactly the same range and hence conversions between them are possible.请记住,将 x 声明为无符号整数会使其自身是有符号对应物的 (+ve) 范围的两倍,但它落在完全相同的范围内,因此它们之间的转换是可能的。

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

Hence for the rest of the code above, it doesn't matter here whether the operands of y and z are signed (+ve/-ve) or unsigned (+ve) because you are dealing with an unsigned integer in the first place, for which the signed integers will be converted to their corresponding unsigned integer versions by adding UINTMAX+1 to the signed version.因此,对于上面的其余代码,y 和 z 的操作数是有符号 (+ve/-ve) 还是无符号 (+ve) 并不重要,因为您首先处理的是无符号整数,通过将UINTMAX+1添加到有符号版本,将有符号整数转换为其相应的无符号整数版本。 Here UINTMAX stands for the largest unsigned value which is added with the smallest power of 2 (2^0=1).这里UINTMAX代表最大的无符号值,它与 2 的最小幂相加 (2^0=1)。 The end result would obviously be larger than values we can store, hence it is taken as modulo to collect the correct unsigned int.最终结果显然会大于我们可以存储的值,因此它被视为模数来收集正确的 unsigned int。

However, if you were to convert x to signed instead:但是,如果您要将 x 转换为有符号:

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

you would get what you might have expected:你会得到你所期望的:

x + y = -294967297

I would also appreciate someone telling me that I won't ever need this kind of arcane knowledge in real life我也很感激有人告诉我在现实生活中我永远不需要这种神秘的知识

There is one implicit promotion not addressed above that is a common cause of bugs:上面没有提到的一个隐式提升是错误的常见原因:

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

The result of this is false because arithmetic operations are implicitly promoted to at least int.这样做的结果是错误的,因为算术运算至少被隐式提升为 int。

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

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