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

Правильный способ абсолютного значения 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...
}
4b9b3361

Ответ 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;
          }
 }