Продвижение в целочисленных арифметических выражениях

У меня есть два связанных вопроса:

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

Первая часть меня не удивляет: как объясняется в разделе "Какие переменные мне следует вводить при выполнении математических операций в C/C++"? во время арифметических операций компилятор неявно продвигает более короткие и длинные целые числа (и я полагаю, что это применимо к операторам сравнения). Это гарантированное стандартное поведение?

Но ответ на этот вопрос, а также ответ на сравнения со знаком и без знака говорят, что целые числа со знаком следует переводить в число без знака. Я ожидал, что x == X выше будет верно, так как они содержат одни и те же данные (см. memcmp). Вместо этого, похоже, происходит то, что оба повышаются до более широких целых чисел, а затем происходит знак со знаком или без знака (или наоборот).

РЕДАКТИРОВАТЬ 2:

В частности меня интересуют случаи, когда, скажем, функция возвращает int это будет -1 в случае ошибки, иначе будет представлять, например, количество записанных байтов, которое всегда должно быть положительным. Стандартные функции этого типа возвращают ssize_t что, если я не ошибаюсь, на большинстве платформ совпадает с int64_t, но количество записанных байтов может быть вплоть до UINT64_MAX, Так что если я хочу сравнить возвращенный int или же ssize_t к значению без знака для ожидаемых записанных байтов, явное приведение к unsigned int или же size_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 ответ

Решение

Имейте в виду, что предыдущий код для Comapre, как 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) как x а также y пройти через целочисленные акции int или же unsigned, 2) Если типы различаются, тот, кто имеет меньший ранг, проходит обычные арифметические преобразования в более высокий, то произведение вычисляется. Если этот продукт численно переполняет целевой тип, то неопределенное поведение в случае подписанных типов и перенос в случае неподписанных типов.

После вычисления продукта сравнение происходит по тем же правилам, что и обычные целочисленные рекламные акции, а затем более высокий рейтинг.

  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 
Другие вопросы по тегам