Как можно безопасно static_cast между unsigned int и int? - PullRequest
11 голосов
/ 29 сентября 2011

У меня есть 8-символьный string, представляющий шестнадцатеричное число, и мне нужно преобразовать его в int.Это преобразование должно сохранять битовую комбинацию для строк "80000000" и выше, т. Е. Эти числа должны получаться отрицательными.К сожалению, наивное решение:

int hex_str_to_int(const string hexStr)
{    
    stringstream strm;
    strm << hex << hexStr;
    unsigned int val = 0;
    strm >> val;
    return static_cast<int>(val);
}

не работает для моего компилятора, если val > MAX_INT (возвращаемое значение равно 0).Изменение типа val на int также приводит к 0 для больших чисел.Я попробовал несколько различных решений из различных ответов здесь, на SO, и пока не добился успеха.

Вот что я знаю:

  • Я использую компилятор HP C ++ наOpenVMS (с использованием, как мне кажется, процессора Itanium).
  • sizeof(int) будет не менее 4 для каждой архитектуры, на которой будет выполняться мой код.
  • Преобразование из числа> INT_MAX в intреализации.На моей машине это обычно приводит к 0, но, что интересно, приведение от long к int приводит к INT_MAX, когда значение слишком велико.

Это удивительно трудно сделать правильно,или, по крайней мере, это было для меня.Кто-нибудь знает решение portable для этого?

Обновление:

Изменение static_cast на reinterpret_cast приводит к ошибке компилятора.Комментарий побудил меня попробовать приведение в стиле C: return (int)val в приведенном выше коде, и это сработало. На этой машине. Будет ли это безопасно на других архитектурах?

Ответы [ 5 ]

11 голосов
/ 29 сентября 2011

Цитирование стандарта C ++ 03, §4.7 / 3 (Интегральные преобразования):

Если тип назначения подписан, значение не изменяется, если оно может быть представлено в типе назначения (и ширине битового поля); в противном случае значение определяется реализацией .

Поскольку результат определяется реализацией, по определению невозможно найти действительно переносимое решение.

8 голосов
/ 29 сентября 2011

Хотя существуют способы сделать это с помощью приведения и преобразования, большинство полагается на неопределенное поведение, которое может иметь четко определенное поведение на некоторых машинах / с некоторыми компиляторами. Вместо того чтобы полагаться на неопределенное поведение, скопируйте данные:

int signed_val;
std::memcpy (signed_val, val, sizeof(int));
return signed_val;
5 голосов
/ 29 сентября 2011

Вы можете отменить число без дополнения до двух без знака, взяв дополнение и добавив его.Итак, давайте сделаем это для негативов:

if (val < 0x80000000) // positive values need no conversion
  return val;
if (val == 0x80000000) // Complement-and-addition will overflow, so special case this
  return -0x80000000; // aka INT_MIN
else
  return -(int)(~val + 1);

Это предполагает, что ваши целые числа представлены 32-битным представлением с двойным дополнением (или имеют аналогичный диапазон).Он не зависит от какого-либо неопределенного поведения, связанного с целочисленным переполнением со знаком (обратите внимание, что поведение целочисленного переполнения без знака четко определено - хотя и здесь этого не должно происходить!).

Примечаниечто если ваши целые числа не 32-битные, все становится сложнее.Возможно, вам придется использовать что-то вроде ~(~0U >> 1) вместо 0x80000000.Кроме того, если ваши целые числа не дополняются двойками, у вас могут возникнуть проблемы с переполнением некоторых значений (например, на машине с одним дополнением -0x80000000 не может быть представлено в 32-разрядном целом числе со знаком).Тем не менее, машины без двойного дополнения сегодня очень редки, так что это вряд ли будет проблемой.

4 голосов
/ 29 сентября 2011

Вот еще одно решение, которое сработало для меня:

if (val <= INT_MAX) {
    return static_cast<int>(val);
}
else {
    int ret = static_cast<int>(val & ~INT_MIN);
    return ret | INT_MIN;
}

Если я маскирую старший бит, я избегаю переполнения при приведении. Тогда я смогу ИЛИ вернуть его благополучно.

0 голосов
/ 02 июля 2019
unsigned int u = ~0U;
int s = *reinterpret_cast<int*>(&u); // -1

В противоположность:

int s = -1;
unsigned int u = *reinterpret_cast<unsigned int*>(&s); // all ones
...