简体   繁体   English

转换有符号和无符号数字

[英]Casting signed and unsigned numbers

I had a function that was producing invalid input when dividing a signed number by an unsigned number.我有一个 function 在将有符号数除以无符号数时产生无效输入。 What this means is that the negative number was being interpreted as unsigned and so was making a calculation invalid.这意味着负数被解释为unsigned数,因此计算无效。 For example, here are the possibilities when dividing between two potentially signed numbers:例如,以下是在两个可能有符号的数字之间进行划分时的可能性:

printf ("%d %d %d %d %d %d %d %d %d",
        (signed)10 / (signed)8,
        (unsigned)10 / (signed)8,
        (unsigned)10 / (unsigned)8,
        
        (signed)-10 / (signed)8,
        (unsigned)-10 / (signed)8,       // bad result
        (unsigned)-10 / (unsigned)8,     // bad result
        
        (signed)-10 / (signed)-8,
        (unsigned)-10 / (signed)-8,     // ??
        (unsigned)-10 / (unsigned)-8    // ??
);

$ run
// 1 1 1 -1 536870910 536870910 1 0 0

What are the rules for when a signed number is divided by an unsigned number?signed数除以unsigned数时的规则是什么? Are both numbers cast to the same type before the calculation?在计算之前,两个数字是否都转换为相同的类型? Or is the result cast?或者结果是什么? It seems in the function I had a signed negative number that was being divided by an unsigned positive number, which I thought would "just work", but it seems them instead of the types being implicitly converted so that my number would be, for example, "-1", the result came out to be a very large positive number.似乎在 function 中我有一个带符号的负数被一个无符号的正数除,我认为这会“正常工作”,但似乎它们而不是隐式转换的类型,因此我的数字将是,例如, "-1", 结果是一个很大的正数。 What are the rules when mixed-sign numbers are operated on?对混合符号数进行运算时的规则是什么?

For each of arguments, three things happen in this order:对于 arguments 中的每一个,按此顺序发生三件事:

  1. The conversions specified by the casts are performed.执行强制转换指定的转换。
  2. The two operands of / are converted to a common type. /的两个操作数被转换为通用类型。
  3. The argument is passed to printf to be converted to text by %d .参数传递给printf以由%d转换为文本。

Let's discuss these in order.让我们按顺序讨论这些。

1. Casts and Conversions 1. 演员表和转换

A conversion is a change of value from one type to another.转换是价值从一种类型到另一种类型的变化。 Where possible, a conversion does not change the value.在可能的情况下,转换不会改变价值。 Otherwise, we desire conversions to change the value as little as possible (converting 3.125 from float to int produces 3, not 87) or in some information-preserving way (conversions from a 32-bit int to a 32-bit unsigned int “wrap” modulo 2 32 ).否则,我们希望转换尽可能少地改变值(将 3.125 从float转换为int产生 3,而不是 87)或以某种信息保留方式(从 32 位int转换为 32 位unsigned int “wrap ” 模 2 32 )。

Some conversions occur automatically.一些转换会自动发生。 For example, in int x = 3.125f;例如int x = 3.125f; , the float value 3.125 is converted to the int value 3. float值 3.125 转换为int值 3。

To explicitly request a conversion, we use a cast.为了显式请求转换,我们使用强制转换。 A cast operator has the form ( type ) expression .强制转换运算符具有形式( type ) expression A cast is specifically this operator.演员表就是这个运算符。 Automatic conversions are not casts.自动转换不是强制转换。

When a signed integer with value x is converted to an n -bit unsigned integer, the result is, the result is what you get by taking x and adding or subtracting 2 n until you get a value that is representable in the unsigned integer type.当带有值x的有符号 integer 转换为n位无符号 integer 时,结果是,通过取x并加上或减去 2 n得到的结果,直到得到一个可在无符号 Z157DB7DF3360023 类型中表示的值。 For example, when −10 is converted to a 32-bit unsigned int , 2 32 (4,294,967,296) is added once, producing −10 + 4,294,967,296 = 4,294,967,286.例如,当 -10 转换为 32 位unsigned int时,将 2 32 (4,294,967,296) 添加一次,产生 -10 + 4,294,967,296 = 4,294,967,286。 When −8 is converted, the result is −8 + 4,294,967,296 = 4,294,967,288.转换 -8 时,结果为 -8 + 4,294,967,296 = 4,294,967,288。

These both had to add 2 32 just once to bring the value in range.这两个都必须添加一次 2 32才能使值在范围内。 To see an example where multiple additions or subtractions are necessary, consider converting 1001 to eight-bit unsigned char .要查看需要多次加法或减法的示例,请考虑将 1001 转换为八位unsigned char The three subtractions of 256 are necessary to bring the result into the range 0 to 255, so the result is 1001 − 3•256 = 1001 − 768 = 233. 256 的三个减法对于将结果带入 0 到 255 范围内是必要的,因此结果为 1001 - 3•256 = 1001 - 768 = 233。

2. The Usual Arithmetic Conversions 2. 通常的算术转换

For many C binary operators, particularly the arithmetic operators, the two operands are converted to a common type perform the operation is performed.对于许多 C 二元运算符,特别是算术运算符,将两个操作数转换为通用类型执行操作。 (This is largely due to how computer hardware works; it is easy to multiply two unsigned numbers or two signed numbers but somewhat awkward to multiply a signed number by an unsigned number.) The rules for this are called the usual arithmetic conversions . (这主要是由于计算机硬件的工作原理;将两个无符号数或两个有符号数相乘很容易,但将一个有符号数乘以一个无符号数则有些尴尬。)这种规则称为通常的算术转换

For example, the specification for the / operator in C 2018 6.5.5 3 says “The usual arithmetic conversions are performed on the operands.”例如,C 2018 6.5.5 3 中/运算符的规范说“通常的算术转换是在操作数上执行的”。 The usual arithmetic conversions are specified in C 2018 6.3.1.8.通常的算术转换在 C 2018 6.3.1.8 中指定。 The rules are expressed with a technical concept of rank and other details but may be summarized for standard real types: If either operand is earlier in the list below than int , it is converted to int .规则用 rank 和其他细节的技术概念表示,但可以针对标准实类型进行总结:如果任一操作数在下面的列表中比int更早,则将其转换为int (This part is called the integer promotions .) After that, if the two operand types are different, the operand with the earlier type is converted to the later type: (这部分称为integer 促销。)之后,如果两个操作数类型不同,则将较早类型的操作数转换为较晚类型:

  • _Bool , signed char , char , unsigned char , short , unsigned short , int , unsigned int , long , unsigned long , long long , unsigned long long , float , double , long double . _Bool 、有signed charcharunsigned charshortunsigned shortintunsigned intlongunsigned longlong longunsigned long longfloatdoublelong double

Here is how this affects your examples, demonstrated with a 32-bit int (aka signed ) and a 32-bit unsigned (aka unsigned int ):以下是这如何影响您的示例,用 32 位int (又名signed )和 32 位unsigned (又名unsigned int )演示:

  • (signed)10 / (signed)8 . (signed)10 / (signed)8 Both operands are int .两个操作数都是int 10/8 is computed, and the result is 1.计算 10/8,结果为 1。
  • (unsigned)10 / (signed)8 . (unsigned)10 / (signed)8 (signed)8 is converted to unsigned , which does not change the value. (signed)8转换为unsigned ,这不会改变值。 10/8 is computed, and the result is 1.计算 10/8,结果为 1。
  • (unsigned)10 / (unsigned)8 . (unsigned)10 / (unsigned)8 Both operands are unsigned int .两个操作数都是unsigned int 10/8 is computed, and the result is 1.计算 10/8,结果为 1。
  • (signed)-10 / (signed)8 . (signed)-10 / (signed)8 Both operands are int .两个操作数都是int −10/8 is computed, and the result is −1.计算-10/8,结果为-1。
  • (unsigned)-10 / (signed)8 . (unsigned)-10 / (signed)8 (unsigned)-10 is 4,294,967,286. (unsigned)-10是 4,294,967,286。 (signed)8 is converted to unsigned int , which does not change the value. (signed)8转换为unsigned int ,这不会更改值。 4,294,967,286/8 is computed, and the result is 536,870,910.计算 4,294,967,286/8,结果为 536,870,910。
  • (unsigned)-10 / (unsigned)8 . (unsigned)-10 / (unsigned)8 (unsigned)-10 is 4,294,967,286. (unsigned)-10是 4,294,967,286。 Both perands are unsigned int .两个 perand 都是unsigned int 4,294,967,286/8 is computed, and the result is 536,870,910.计算 4,294,967,286/8,结果为 536,870,910。
  • (signed)-10 / (signed)-8 . (signed)-10 / (signed)-8 Both operands are int .两个操作数都是int −10/−8 is computed, and the result is 1.计算-10/-8,结果为1。
  • (unsigned)-10 / (signed)-8 . (unsigned)-10 / (signed)-8 (unsigned)-10 is 4,294,967,286. (unsigned)-10是 4,294,967,286。 (signed)−8 is converted to unsigned int , which changes the value to 4,294,967,288. (signed)−8转换为unsigned int ,这会将值更改为 4,294,967,288。 4,294,967,286/4,294,967,288 is computed, and the result is 0.计算 4,294,967,286/4,294,967,288,结果为 0。
  • (unsigned)-10 / (unsigned)-8 . (unsigned)-10 / (unsigned)-8 (unsigned)-10 is 4,294,967,286. (unsigned)-10是 4,294,967,286。 (unsigned)−8 is 4,294,967,288. (unsigned)−8是 4,294,967,288。 4,294,967,286/4,294,967,288 is computed, and the result is 0.计算 4,294,967,286/4,294,967,288,结果为 0。

3. printf Conversion Specifiers 3. printf转换说明符

The result of the division operator, / , has the same type as the operands after the usual arithmetic conversions.除法运算符/的结果与通常算术转换后的操作数具有相同的类型。 So some of your arguments have type int , and some have type unsigned int .所以你的一些 arguments 有int类型,有些有unsigned int类型。

When %d is used in the printf format, the corresponding argument is supposed to be an int .%d用于printf格式时,对应的参数应该是一个int This is specified in C 2018 7.21.6.1 8 (which is about fprintf , but the fprintf clause refers to it).这是在 C 2018 7.21.6.1 8 中指定的(大约fprintf ,但fprintf子句引用了它)。 Paragraph 9 says “… If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.”第 9 段说“……如果任何参数不是相应转换规范的正确类型,则行为未定义。”

So, formally, the behavior of your program is not defined by the C standard.因此,正式地,您的程序的行为不是由 C 标准定义的。 However, C implementations often let this substitution of an unsigned int argument for an int argument slide, and the program “works” due to a combination of historical reasons and how software and hardware work.然而,C 实现经常让这种用unsigned int参数替换int参数滑动,并且由于历史原因以及软件和硬件如何工作的组合,程序“工作”。 (This substitution is explicitly allowed when calling a function declared without a prototype, in C 2018 6.5.2.2 6. One might argue that if you did not include <stdio.h> and declared printf yourself with int printf(); , it would be allowed by that rule.) (This substitution is explicitly allowed when calling a function declared without a prototype, in C 2018 6.5.2.2 6. One might argue that if you did not include <stdio.h> and declared printf yourself with int printf(); , it would被该规则允许。)

So, in spite of having behavior not defined by the C standard, it is unsurprising your program prints the results described above.因此,尽管 C 标准未定义行为,但您的程序打印上述结果也就不足为奇了。 However, it would be better to change %d to %u for the cases where the argument is unsigned int .但是,对于参数为unsigned int的情况,最好将%d更改为%u

With (unsigned)-10 , what you get at compilation is 4294967295 (uint32_t max value, due to the underflow), minus 10 , then divided by 8, which is indeed 536870910.使用(unsigned)-10 ,您在编译时得到的是4294967295 (uint32_t max value, due to the underflow), minus 10 ,然后除以 8,这确实是 536870910。

As for your different divisions, the result is standard defined (credit @Barmar)至于您的不同部门,结果是标准定义的(信用@Barmar)

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

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