【问题标题】:Promotion in integer arithmetic expressions整数算术表达式的提升
【发布时间】:2017-11-23 00:12:37
【问题描述】:

我有两个相关的问题:

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

  2. 在具有相同底层二进制值的等宽有符号和无符号整数之间进行比较呢?

下面的例子可以说明我的意思

#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位的情况一样吗?

【问题讨论】:

  • 车轮已经在int8_t X = x; 处脱落。您正在尝试将 250 的无符号八位值填充到 127 处。相反(将有符号填充为无符号)具有明确的转换规则(添加 max-type-val +1 直到它落在范围内) ,但你正在做相反的事情。
  • 为什么未定义?我的印象是 250 将被转换为 int8_t ,即 -6。打印 X 的值(已签名)确实显示 -6。
  • 那么“标准怎么说?”的答案是什么?没什么”?并且必须在X == x 中的比较两侧使用显式转换?
  • Edit 2 应该是一个新问题。但是你的代码看起来像ssize_t x = foo(); if ( x &lt; 0 ) { errorhandling } else if ( x == expected_unsigned_value ) ...

标签: c integer-arithmetic


【解决方案1】:

请注意,之前的代码如 uint8_t x = 250; ... int8_t X = x; 是实现定义的 @David Bowling

  1. x * y == z(或 x + y == z)形式的算术表达式之间的比较,其中 x * y 太大,x 或 y 都无法容纳,但不大于 z。

x * y 的乘积是在不考虑z 的情况下计算的。产品类型由 1) xy 都经过整数促销intunsigned。 2) 如果类型不同,则 rank 中较小的一个通过 通常的算术转换 到较高的一个,然后计算乘积。如果该产品在数字上溢出目标类型,则 未定义的行为 在有符号类型的情况下和在无符号类型的情况下环绕。

计算产品后,比较遵循通常的整数促销相同的规则,然后是更高的排名。

  1. 在具有相同底层二进制值的等宽有符号和无符号整数之间进行比较会怎样?

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

任何 2 个相同的二进制 总是比较相同。 在 __integer Promotions_ 之后,如果有符号类型是常见的 2 的补码,则任何 2 个相同宽度的二进制 位模式(不包括稀有填充位)(不包括稀有填充位)将比较相同.

int8_t i = -1;
uint8_t u = i;         // u =  255
if (i == u) --> false  // both i,x promoted to int and -1 != 255)

int i = -1;
unsigned u = i;        // u = UINT_MAX  
if (i == u) --> true   // i promoted to unsigned and value UINT_MAX 

【讨论】:

  • 整数提升通常的算术转换
  • @M.M 是的,这听起来是正确的。答案已修改。如果您发现其他缺点,请编辑。
猜你喜欢
  • 1970-01-01
  • 2011-01-25
  • 1970-01-01
  • 2017-08-26
  • 1970-01-01
  • 2015-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多