Как я могу портативно узнать наименьшее из 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));
}