Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

Пока типичное значение 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, С++ наследует этот материал):

Содержимое заголовка приведено ниже, в алфавитном порядке заказ. Показанные минимальные величины должны быть заменены на определяемые реализацией величины с одним знаком. Значения должны все они являются постоянными выражениями, подходящими для использования в #if preprocessing директивы. Компоненты описаны далее в разделе 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), вступает в игру и дополнительно ограничивает это тем, что мы знаем как дополнение к двум или одним:

Для знаковых целых типов биты представления объекта должны быть разделены на три группы: биты значений, биты заполнения и знаковый бит. Не должно быть никаких битов заполнения; подписанный char не должен иметь никаких битов заполнения. Должен быть ровно один знаковый бит. Каждый бит, который является битом значения, должен иметь то же значение, что и один бит в объекте представление соответствующего неподписанного типа (если в подписанных тип и 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], поведение undefined. Это прямо указано в С++ в пункте 5/4:

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

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

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

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

Поэтому нам нужно избегать вычисления значения, которое не может находиться в диапазоне [INT_MIN, INT_MAX]. Lucky, 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.

Ответ 2

INT_MAX + INT_MIN < 0 ? INT_MAX : -INT_MIN

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

Ответ 3

В C99 и выше INT_MAX.

Введите спецификацию:

Для знаковых целых типов биты представления объекта должны быть разделены на три группы: биты значений, биты заполнения и знаковый бит. Не должно быть никаких битов заполнения; подписанный char не должен иметь никаких битов заполнения. Должен быть ровно один знаковый бит. Каждый бит, который является битом значения, должен иметь то же значение, что и один бит в объекте представление соответствующего неподписанного типа (если в подписанных тип и 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)

Ответ 4

Насколько я знаю,

-INT_MAX представляется как int во всех диалектах C и С++. Поэтому:

-INT_MAX <= INT_MIN ? -INT_MIN : INT_MAX

Ответ 5

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

Ответ 6

abs(INT_MIN) будет вызывать поведение undefined. Стандарт говорит

7.22.6.1 Функции abs, labs и llabs:

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

Попробуйте это вместо:
Преобразуйте 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));
}