Правильный способ абсолютного значения INT_MIN
Я хочу выполнить некоторую арифметику в unsigned и нужно принять абсолютное значение отрицательного int, что-то вроде
do_some_arithmetic_in_unsigned_mode(int some_signed_value)
{
unsigned int magnitude;
int negative;
if(some_signed_value<0) {
magnitude = 0 - some_signed_value;
negative = 1;
} else {
magnitude = some_signed_value;
negative = 0;
}
...snip...
}
Но INT_MIN может быть проблематичным, 0 - INT_MIN является UB, если выполняется в подписанной арифметике.
Что такое стандартный/надежный/безопасный/эффективный способ сделать это в C?
EDIT:
Если мы знаем, что мы находимся в 2-дополнении, возможно, неявный литой и явный бит ops будет стандартным? если возможно, я хотел бы избежать этого предположения.
do_some_arithmetic_in_unsigned_mode(int some_signed_value)
{
unsigned int magnitude=some_signed_value;
int negative=some_signed_value<0;
if (negative) {
magnitude = (~magnitude) + 1;
}
...snip...
}
Ответы
Ответ 1
Преобразование из подписанного в unsigned корректно: вы получаете соответствующий представитель по модулю 2 N. Поэтому следующее даст вам правильное абсолютное значение n
:
int n = /* ... */;
unsigned int abs_n = n < 0 ? UINT_MAX - ((unsigned int)(n)) + 1U
: (unsigned int)(n);
Обновление. Как показано в @aka.nice, мы можем фактически заменить UINT_MAX + 1U
на 0U
:
unsigned int abs_n = n < 0 : -((unsigned int)(n)) : (unsigned int)(n);
Ответ 2
В отрицательном случае возьмите some_signed_value+1
. Отмените его (это безопасно, потому что оно не может быть INT_MIN
). Преобразовать в unsigned. Затем добавьте один;
Ответ 3
Вы всегда можете проверить >= -INT_MAX
, это всегда хорошо определено. Единственный случай для вас интересен, если INT_MIN < -INT_MAX
и some_signed_value == INT_MIN
. Вам придется протестировать этот случай отдельно.
Ответ 4
static unsigned absolute(int x)
{
if (INT_MIN == x) {
/* Avoid tricky arithmetic overflow possibilities */
return ((unsigned) -(INT_MIN + 1)) + 1U;
} else if (x < 0) {
return -x;
} else {
return x;
}
}