Как я могу портативно узнать самый маленький из INT_MAX
и абс (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, 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)
вызовет неопределенное поведение. Стандарт говорит
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));
}