Как я могу портативно узнать наименьшее из INT_MAX
и abs (INT_MIN
)? (Это математическое абсолютное значение INT_MIN
, а не вызов функции abs
.)
Он должен быть таким же, как INT_MAX
в большинстве систем, но я ищу более портативный способ.
Как я могу портативно узнать наименьшее из INT_MAX
и abs (INT_MIN
)? (Это математическое абсолютное значение INT_MIN
, а не вызов функции abs
.)
Он должен быть таким же, как INT_MAX
в большинстве систем, но я ищу более портативный способ.
Пока типичное значение 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
.
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
является правильным ответом.
В 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)
-INT_MAX
представляется как int
во всех диалектах 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)
будет вызывать поведение undefined. Стандарт говорит
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));
}