Как безопасно static_cast между unsigned int и int?
У меня есть 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 С++ на OpenVMS (используя, я полагаю, процессор Itanium).
-
sizeof(int)
будет по крайней мере 4 на каждой архитектуре, на которой будет работать мой код.
- Выделение из числa > INT_MAX в int определяется реализацией. На моей машине это обычно приводит к 0, но интересное кастинг от
long
до int
приводит к INT_MAX
, когда значение слишком велико.
Это удивительно сложно сделать правильно, или, по крайней мере, это было для меня. Кто-нибудь знает о переносном решении для этого?
Update:
Изменение static_cast
до reinterpret_cast
приводит к ошибке компилятора. Комментарий заставил меня попробовать C-стиль: return (int)val
в коде выше, и он сработал. На этой машине. Будет ли это безопасно на других архитектурах?
Ответы
Ответ 1
В то время как есть способы сделать это с помощью приемов и преобразований, большинство полагается на поведение undefined, которое имеет хорошо определенные поведения на некоторых машинах/с некоторыми компиляторами. Вместо того, чтобы полагаться на поведение undefined, скопируйте данные:
int signed_val;
std::memcpy (signed_val, val, sizeof(int));
return signed_val;
Ответ 2
Цитирование стандарта С++ 03, §4.7/3 (интегральные преобразования):
Если тип назначения подписан, значение не изменяется, если оно может быть представлено в типе назначения (и ширине битового поля); в противном случае значение определяется реализацией.
Поскольку результат определяется реализацией, по определению невозможно, чтобы было действительно портативное решение.
Ответ 3
Вы можете свести на нет номер двоеточия без знака, взяв дополнение и добавив его. Так что сделайте это для негативов:
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);
Это предполагает, что ваш ints представлен 32-битным представлением двойного дополнения (или имеет аналогичный диапазон). Он не полагается на поведение undefined, связанное с переполнением целых чисел (обратите внимание, что поведение неподписанного целочисленного переполнения определено корректно), хотя это тоже не должно быть!).
Обратите внимание, что если ваши ints не являются 32-битными, все становится более сложным. Возможно, вам нужно использовать что-то вроде ~(~0U >> 1)
вместо 0x80000000
. Кроме того, если ваши ints не являются двумя дополнениями, у вас могут возникнуть проблемы с переполнением определенных значений (например, на машине с одним дополнением, -0x80000000
не может быть представлено в 32-разрядном значении целого числа). Тем не менее, машины без двухкомпонентного оборудования сегодня очень редки, поэтому это вряд ли будет проблемой.
Ответ 4
Здесь другое решение, которое сработало для меня:
if (val <= INT_MAX) {
return static_cast<int>(val);
}
else {
int ret = static_cast<int>(val & ~INT_MIN);
return ret | INT_MIN;
}
Если я маскирую высокий бит, я избегаю переполнения при кастинге. Тогда я смогу сделать это безопасно.
Ответ 5
unsigned int u = ~0U;
int s = *reinterpret_cast<int*>(&u); // -1
Сontrariwise:
int s = -1;
unsigned int u = *reinterpret_cast<unsigned int*>(&s); // all ones