Продвижение в целочисленных арифметических выражениях
У меня есть два связанных вопроса:
Что говорится в стандарте, и что делают разные компиляторы, когда дело доходит до сравнения арифметического выражения в форме x * y == z (или x + y == z), где x * y слишком велико для любого x или y для удержания, но не больше z.
Как насчет сравнения целых чисел со знаком и без знака одинаковой ширины с одним и тем же базовым двоичным значением?
Пример ниже может прояснить, что я имею в виду
#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
- сравнение между арифметическим выражением вида x * y == z (или x + y == z), где x * y слишком велико, чтобы удерживать x или y, но не больше z.
Продукт x * y
вычисляется без учета z
, Тип продукта определяется 1) как x
а также y
пройти через целочисленные акции int
или же unsigned
, 2) Если типы различаются, тот, кто имеет меньший ранг, проходит обычные арифметические преобразования в более высокий, то произведение вычисляется. Если этот продукт численно переполняет целевой тип, то неопределенное поведение в случае подписанных типов и перенос в случае неподписанных типов.
После вычисления продукта сравнение происходит по тем же правилам, что и обычные целочисленные рекламные акции, а затем более высокий рейтинг.
- Как насчет сравнения целых чисел со знаком и без знака одинаковой ширины с одним и тем же базовым двоичным значением?
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