Как портативно узнать min(INT_MAX, abs(INT_MIN))?

Как я могу портативно узнать самый маленький из INT_MAX и абс (INT_MIN)? (Это математическое абсолютное значение INT_MINне призыв к abs функция).

Это должно быть так же, как INT_MAX в большинстве систем, но я ищу более портативный способ.

6 ответов

Решение

Хотя типичное значение INT_MIN -2147483648, а типичное значение INT_MAX 2147483647, это не гарантируется стандартом. TL;DR: значение, которое вы ищете INT_MAX в соответствующей реализации. Но расчет min(INT_MAX, abs(INT_MIN)) не переносимый


Возможные значения INT_MIN а также INT_MAX

INT_MIN а также INT_MAX определены в Приложении E (Пределы реализации) 1 (стандарт C, C++ наследует этот материал):

Содержимое заголовка приведено ниже в алфавитном порядке. Показанные минимальные величины должны быть заменены величинами, определенными реализацией с тем же знаком. Все значения должны быть константными выражениями, подходящими для использования в директивах предварительной обработки #if. Компоненты описаны далее в 5.2.4.2.1.

[...]

#define INT_MAX +32767

#define INT_MIN -32767

[...]

Стандарт требует тип int быть целочисленным типом, который может представлять диапазон [INT_MIN, INT_MAX] (раздел 5.2.4.2.1.).

Тогда 6.2.6.2. (Целочисленные типы, опять же часть стандарта C), вступает в игру и еще более ограничивает это тем, что мы знаем как дополнение двух или единиц:

Для целых типов со знаком биты представления объекта должны быть разделены на три группы: биты значения, биты заполнения и бит знака. Там не должно быть никаких битов заполнения; знаковый символ не должен иметь никаких битов заполнения. Должен быть ровно один знаковый бит. Каждый бит, который является битом значения, должен иметь то же значение, что и тот же бит в представлении объекта соответствующего типа без знака (если имеется M битов значения в типе со знаком и N в типе без знака, то M ≤ N). Если бит знака равен нулю, он не должен влиять на результирующее значение. Если бит знака равен единице, значение должно быть изменено одним из следующих способов:

- соответствующее значение со знаковым битом 0 обнуляется (знак и величина);

- знаковый бит имеет значение - (2M) (дополнение до двух);

- знаковый бит имеет значение - (2M - 1) (дополнение).

Раздел 6.2.6.2. Также очень важно связать представление значения целочисленных типов со знаком с представлением значения его беззнаковых братьев и сестер.

Это означает, что вы либо получите диапазон [-(2^n - 1), (2^n - 1)] или же [-2^n, (2^n - 1)], где n обычно 15 или 31.

Операции со знаковыми целочисленными типами

Теперь для второго: операции со знаковыми целочисленными типами, которые приводят к значению, которое находится за пределами диапазона [INT_MIN, INT_MAX], поведение не определено. Это явно предписано в C++ пунктом 5/4:

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

Для C 6.5/5 предлагает очень похожий отрывок:

Если во время вычисления выражения возникает исключительное условие (то есть, если результат не определен математически или не находится в диапазоне представимых значений для его типа), поведение не определено.

Так что же произойдет, если значение INT_MIN бывает меньше, чем негатив INT_MAX (например, -32768 и 32767 соответственно)? расчета -(INT_MIN) будет неопределенным, так же, как INT_MAX + 1,

Таким образом, мы должны избегать вычисления значения, которое может не находиться в диапазоне [INT_MIN, INT_MAX], Счастливый, INT_MAX + INT_MIN всегда в этом диапазоне, так как INT_MAX является строго положительным значением и INT_MIN строго отрицательное значение. следовательно INT_MIN < INT_MAX + INT_MIN < INT_MAX,

Теперь мы можем проверить, INT_MAX + INT_MIN равно, меньше или больше 0.

`INT_MAX + INT_MIN`  |  value of -INT_MIN    | value of -INT_MAX 
------------------------------------------------------------------
         < 0         |  undefined            | -INT_MAX
         = 0         |  INT_MAX = -INT_MIN   | -INT_MAX = INT_MIN
         > 0         |  cannot occur according to 6.2.6.2. of the C standard

Следовательно, чтобы определить минимум INT_MAX а также -INT_MIN (в математическом смысле) достаточно следующего кода:

if ( INT_MAX + INT_MIN == 0 )
{
    return INT_MAX; // or -INT_MIN, it doesn't matter
}
else if ( INT_MAX + INT_MIN < 0 )
{
    return INT_MAX; // INT_MAX is smaller, -INT_MIN cannot be represented.
}
else // ( INT_MAX + INT_MIN > 0 )
{
    return -INT_MIN; // -INT_MIN is actually smaller than INT_MAX, may not occur in a conforming implementation.
}

Или, чтобы упростить:

return (INT_MAX + INT_MIN <= 0) ? INT_MAX : -INT_MIN;

Значения в троичном операторе будут оцениваться только при необходимости. Следовательно, -INT_MIN либо оставляется без оценки (поэтому не может генерировать UB), либо является четко определенным значением.

Или, если вы хотите утверждение:

assert(INT_MAX + INT_MIN <= 0);
return INT_MAX;

Или, если вы хотите это во время компиляции:

static_assert(INT_MAX + INT_MIN <= 0, "non-conforming implementation");
return INT_MAX;

Правильное выполнение целочисленных операций (например, если правильность имеет значение)

Если вы заинтересованы в безопасной целочисленной арифметике, взгляните на мою реализацию безопасных целочисленных операций. Если вы хотите увидеть шаблоны (а не вывод длинного текста), в которых операции не выполняются, а какие успешны, выберите эту демонстрацию.

В зависимости от архитектуры могут быть другие параметры для обеспечения корректности, такие как опция gcc -ftrapv,

INT_MAX + INT_MIN < 0 ? INT_MAX : -INT_MIN

Отредактировано, чтобы добавить объяснение: Конечно, проблема в том, что -INT_MIN или же abs(INT_MIN) будет неопределенным, если -INT_MIN слишком большой, чтобы поместиться в int, Поэтому нам нужен способ проверить, так ли это на самом деле. Состояние INT_MAX + INT_MIN < 0 проверяет ли -INT_MIN больше, чем INT_MAX, Если это так, то INT_MAX является меньшим из двух абсолютных значений. Если нет то INT_MAX больше двух абсолютных значений, и -INT_MIN это правильный ответ.

В С99 и выше, INT_MAX,

Quot спецификации:

Для целых типов со знаком биты представления объекта должны быть разделены на три группы: биты значения, биты заполнения и бит знака. Там не должно быть никаких битов заполнения; знаковый символ не должен иметь никаких битов заполнения. Должен быть ровно один знаковый бит. Каждый бит, который является битом значения, должен иметь то же значение, что и тот же бит в представлении объекта соответствующего типа без знака (если имеется M битов значения в типе со знаком и N в типе без знака, то M ≤ N). Если бит знака равен нулю, он не должен влиять на результирующее значение. Если бит знака равен единице, значение должно быть изменено одним из следующих способов:

  • соответствующее значение со знаковым битом 0 обнуляется (знак и величина);
  • знаковый бит имеет значение - (2^M) (дополнение до двух);
  • знаковый бит имеет значение - (2 ^ M - 1) (дополнение).

(Раздел 6.2.6.2 из http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf)

-INT_MAX представима как int насколько я знаю, на всех диалектах C и C++. Следовательно:

-INT_MAX <= INT_MIN ? -INT_MIN : INT_MAX

В большинстве систем abs (INT_MIN) не определена. Например, на типичных 32-битных компьютерах INT_MAX = 2^31 - 1, INT_MIN = - 2^31 и abs (INT_MIN) не могут быть 2^31.

abs(INT_MIN) вызовет неопределенное поведение. Стандарт говорит

7.22.6.1 abs, labs а также llabs функции:

abs, labs, а также llabs функции вычисляют абсолютное значение целого числа j, Если результат не может быть представлен, поведение не определено.

Попробуйте это вместо этого:
Перерабатывать INT_MIN в unsignrd int, Поскольку -ve числа не могут быть представлены как unsigned int, INT_MAX будет преобразован в UINT_MAX + 1 + INT_MIN,

#include <stdio.h>
#include <stdlib.h>

unsigned min(unsigned a, unsigned b)
{
    return a < b ? a : b;
}

int main(void)
{
    printf("%u\n", min(INT_MAX, INT_MIN));
}  
Другие вопросы по тегам