整数算术表达式的升级

时间:2017-11-23 00:12:37

标签: c integer-arithmetic

我有两个相关的问题:

  1. 当涉及x * y == z(或x + y == z)形式的算术表达式之间的比较时,标准说什么,以及不同的编译器做了什么,其中x * y对于x或y来说太大而不能保持,但不大于z。

  2. 如何比较具有相同基础二进制值的等宽的有符号和无符号整数?

  3. 下面的例子可以澄清我的意思

    #include <stdio.h>
    #include <stdint.h>
    #include <string.h>
    
    int main (void)
    {
        uint8_t x = 250;
        uint8_t y = 5;
        uint16_t z = x*y;
        uint8_t w = x*y;
    
        if (x * y == z)            // true
            puts ("x*y = z");
        if ((uint16_t)x * y == z)  // true
            puts ("(uint16_t)x*y = z");
        if (x * y == (uint8_t)z)   // false
            puts ("x*y = (uint8_t)z");
        if (x * y == w)            // false
            puts ("x*y = w");
        if ((uint8_t)(x * y) == w) // true
            puts ("(uint8_t)x*y = w");
        if (x * y == (uint16_t)w)  // false
            puts ("x*y = (uint16_t)w");
    
        int8_t X = x;
        if (x == X)                // false
            puts ("x = X");
        if (x == (uint8_t)X)       // true
            puts ("x = (uint8_t)X");
        if ((int8_t)x == X)        // true
            puts ("(int8_t)x = X");
        if (memcmp (&x, &X, 1) == 0) // true
            puts ("memcmp: x = X");
    }
    

    第一部分并不让我感到惊讶:正如Which variables should I typecast when doing math operations in C/C++?中所解释的那样,编译器在算术运算期间隐式地提升了更短到更长的整数(我想这适用于比较运算符)。这是保证的标准行为吗?

    但是这个问题的答案以及Signed/unsigned comparisons的答案都表明签名整数应该提升为无符号整数。我期待上面的x == X是真的,因为它们拥有相同的数据(参见memcmp)。似乎发生的事情是,两者都被提升为更宽的整数,然后签名到无符号(或反之亦然)发生。

    编辑2:

    特别是我感兴趣的是一个函数返回int的情况,如果出现错误将为-1,否则将表示例如写入的字节数,应始终为正数。此类型的标准函数返回ssize_t,如果我在大多数平台上没有弄错,则与int64_t相同,但写入的字节数可以一直到UINT64_MAX。因此,如果我想将返回的intssize_t与写入的预期字节的无符号值进行比较,则显式转换为unsigned intsize_t(如果我没有记错的话)需要与ssize_t相同的宽度但是无符号)?

    编辑1:

    我无法理解以下内容:

    #include <stdio.h>
    #include <stdint.h>
    
    int main (void)
    {
        int8_t ssi = UINT8_MAX;
        uint8_t ssu = ssi;
        printf ("ssi = %hhd\n", ssi); // -1
        printf ("ssu = %hhu\n", ssu); // 255
        if (ssi == ssu) // false
            puts ("ssi == ssu");
    
        puts ("");
        int16_t si = UINT16_MAX;
        uint16_t su = si;
        printf ("si = %hd\n", si); // -1
        printf ("su = %hu\n", su); // 65535
        if (si == su) // false
            puts ("si == su");
    
        puts ("");
        int32_t i = UINT32_MAX;
        uint32_t u = i;
        printf ("i = %d\n", i); // -1
        printf ("u = %u\n", u); // 4294967295
        if (i == u)   // true????
            puts ("i == u");
    
        puts ("");
        int64_t li = UINT64_MAX;
        uint64_t lu = li;
        printf ("li = %ld\n", li); // -1
        printf ("lu = %lu\n", lu); // 18446744073709551615
        if (li == lu) // true
            puts ("li == lu");
    }
    

    虽然可以通过没有更宽的整数来解释64位示例,但32位示例是反直觉的。它不应该与8位和16位情况相同吗?

1 个答案:

答案 0 :(得分:3)

请注意,像[{1}}这样的comapre的先前代码是实现定义@David Bowling

  
      
  1. 形式x * y == z(或x + y == z)的算术表达式之间的比较,其中x * y对于x或y来说太大而不能保持,但不大于z。
  2.   

计算uint8_t x = 250; ... int8_t X = x;的乘积而不考虑x * y。产品的类型由以下各项确定:1)zx通过整数促销yint。 2)如果类型不同,较小的 rank 中的一个通过通常的算术转换到较高的那个,然后计算产品。如果该产品在数字上溢出目标类型,则在签名类型的情况下未定义行为,在无符号类型的情况下为环绕。

在计算产品后,比较符合通常的整数促销的相同规则,然后是更高的等级。

  
      
  1. 如何比较具有相同基础二进制值的等宽的有符号和无符号整数?
  2.   

2个参数,独立完成整数提升。如果它们的符号不同,则签名的那个将转换为匹配的无符号类型。然后比较这些值。

任何2个相同的二进制将始终进行比较。 __integer promotion_之后,如果有符号类型是常见的2的补码,任何2个相同宽度的二进制位模式(不计算稀有填充位)将相同

unsigned