интерпретировать неподписанный как подписанный

Я работаю над встроенной платформой (ARM) и должен быть осторожен при работе с битовыми шаблонами. Давайте представим, что эта строка находится вне моего влияния:

uint8_t foo = 0xCE;          // 0b11001110

Интерпретируется как беззнаковое, это будет 206. Но на самом деле оно подписано, поэтому похоже на -50. Как я могу продолжать использовать это значение как подписанное?

int8_t bar = foo;            // doesn't work

ни то, ни другое (в результате 0x10 или 0x00 для всех входных значений)

int8_t bar = static_cast<int8_t>(foo);
int8_t bar = reinterpret_cast<int8_t&>(foo);

Я просто хочу, чтобы биты оставались нетронутыми, т.е. (bar == 0xCE)

Наоборот, мне было бы интересно, как получить битовые шаблоны, представляющие отрицательные числа, в переменные без знака, не испортив битовый шаблон. Я использую GCC.


person Sven-de    schedule 10.09.2011    source источник
comment
Преобразование из знакового в беззнаковое сохранит битовый шаблон, поэтому знаковый символ -50 станет 206 беззнаковым. Переход от неподписанного к подписанному будет одним и тем же числом, если он может быть представлен как подписанный, в противном случае он определяется реализацией.   -  person JohnPS    schedule 11.09.2011
comment
Мой отладчик меня обманул, int8_t bar = foo; отлично работает на моей платформе. Извините за это, но все же большое спасибо за понимание, которое оно дало мне.   -  person Sven-de    schedule 12.09.2011
comment
@Sven-de: да, это может быть определено реализацией, но большинство реализаций пойдут по простому пути (вместо того, чтобы насытить его до максимума +127)   -  person Jason S    schedule 12.09.2011
comment
@ Sven-de: Если это полезный для вас ответ, не могли бы вы либо поместить его в качестве ответа и принять его, либо принять очень похожий ответ Керрека с комментарием? Сбивает с толку то, что этот вопрос помечен как не имеющий принятого ответа, когда на него уже дан ответ. Спасибо!   -  person Brooks Moses    schedule 15.09.2011


Ответы (5)


Следующее работает отлично для меня, как и должно хотя, как говорят комментарии, это определяется реализацией:

int x = (signed char)(foo);

В C++ вы также можете сказать:

int x = static_cast<signed char>(foo);

Обратите внимание, что продвижение всегда пытается сохранить значение перед переинтерпретацией битовых шаблонов. Таким образом, вам сначала нужно выполнить приведение к подписанному типу того же размера, что и ваш неподписанный тип, чтобы принудительно выполнить подписанную реинтерпретацию.

(Обычно я сталкиваюсь с противоположной проблемой, когда пытаюсь напечатать char в виде пар шестнадцатеричных цифр.)

person Kerrek SB    schedule 10.09.2011
comment
Технически это UB. Это приводит к переполнению. - person Oliver Charlesworth; 10.09.2011
comment
Интересно - я думал, что это четко определено ... как насчет другого способа, приведения знакового символа к беззнаковому? - person Kerrek SB; 10.09.2011
comment
преобразование беззнакового в подписанное определяется реализацией, если число не может быть представлено как знаковое. со знаком без знака становится наименьшим целым числом без знака, конгруэнтным исходному целому числу (по модулю 2 ^ n). В двух дополнительных представлениях это просто сохраняет один и тот же битовый шаблон. См. 4.7 Интегральные преобразования. - person JohnPS; 11.09.2011
comment
@JohnPS: Понятно, спасибо. Я отредактировал ответ, чтобы сказать, что он определяется реализацией. - person Kerrek SB; 11.09.2011

uint8_t foo = 0xCE;          // 0b11001110
int8_t bar;
memcpy( &bar, &foo, 1 );

У него даже есть дополнительный бонус: 99% компиляторов полностью оптимизируют вызов memcpy...

person Goz    schedule 10.09.2011

Что-то некрасивое в этом духе?

int8_t bar = (foo > 127) ? ((int)foo - 256) : foo;

Не полагается на преобразование, поведение которого не определено.

person Oliver Charlesworth    schedule 10.09.2011
comment
Я думаю, вы, возможно, имели в виду 256, а не 128, да? И нет необходимости в уродливом приведении C. - person Cheers and hth. - Alf; 10.09.2011
comment
@Alf: Действительно, вы, вероятно, правы в том, что обычные арифметические преобразования означают, что приведение (будь то стиль C или C ++) не требуется. Но эти правила трудно запомнить, поэтому я бы предпочел оставить их явными. - person Oliver Charlesworth; 10.09.2011

С GCC есть вероятность, что беззнаковые значения являются двумя дополнительными, даже на вашей встроенной платформе.

Тогда 8-битное число 0xCE представляет 0xCE-256.

Потому что дополнение до двух на самом деле просто по модулю 2n, где n — количество битов в представлении.

EDIT: хм, ради репутации я лучше приведу конкретный пример:

int8_t toInt8( uint8_t x )
{
    return (x >= 128? x - 256 : x);
}

EDIT 2: я не видел последнего вопроса о том, как получить битовый шаблон в беззнаковой переменной. Это очень просто: просто назначьте. Результат гарантируется стандартом C++, а именно то, что сохраненное значение конгруэнтно (равно по циферблату) присвоенному значению по модулю 2n.

Ура и чт.,

person Cheers and hth. - Alf    schedule 10.09.2011
comment
Вы имели в виду, что значения со знаком являются дополнением до двух? Значения без знака, конечно, не имеют концепции знака и других различий в представлении битов. - person underscore_d; 24.07.2016

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

uint8_t foo = 0xCE;          
int8_t bar = *reinterpret_cast<int8_t*>(&foo);
person D Krueger    schedule 11.09.2011
comment
Это нарушает строгое использование псевдонимов, поэтому его можно оптимизировать прямо из программы или иным образом UBify. Единственный стандартный способ «переинтерпретировать» представление требует тривиально копируемых типов и memcpy в место назначения. Надеюсь, компилятор оптимизирует это для переинтерпретации машинного кода, которую вы всегда хотели, но которую reinterpret_cast (возможно, к сожалению) не гарантирует. Это очень неправильно понятое ключевое слово, вероятно, потому, что строгие псевдонимы делают его почти бесполезным по сравнению с ожиданиями. Исключением является то, что reinterpret_castпереход к типу char для чтения представления всегда действителен. - person underscore_d; 24.07.2016
comment
@underscore_d Я думал, что это разрешено как тип со знаком или без знака, соответствующий динамическому типу объекта. Однако, возможно, это просто мое неправильное прочтение пункта. - person D Krueger; 25.07.2016
comment
Ага, кажется, ты прав. Спасибо за напоминание об этой строке со знаком/без знака. Однако reinterpret_cast здесь не нужен и, вероятно, обеспечивает более низкую вероятность (гарантию) успеха, чем простой static_cast: stackoverflow.com/a/1751368 /2757035 ... или я просто не могу найти спецификацию того, как reinterpret_cast преобразует знаки? Если не указано, я бы предположил, что он делает (A) все, что хочет... или, по крайней мере, (B) то, что делает static_cast, и в этом случае нет необходимости в обходе указателя. - person underscore_d; 25.07.2016
comment
@underscore_d static_cast в указателе не будет работать, потому что uint8_t и int8_t являются разными типами, поэтому здесь требуется reinterpret_cast. N3797 говорит: указатель объекта может быть явно преобразован в указатель объекта другого типа. Использование указателя вместо значения связано с тем, что в исходном вопросе указывалось, что битовый шаблон должен быть одинаковым в значениях без знака и со знаком. С дополнением до двух это не имеет значения, но для других представлений требуется использование указателя. - person D Krueger; 25.07.2016
comment
Как указано в ответе, который я связал, я говорил о static_cast между значениями, а не об указателях. И с другими представлениями, AFAICT Стандарт только говорит, что сопоставление, выполняемое reinterpret_cast, в лучшем случае определяется реализацией, что, похоже, не гарантирует строго того, что хочет OP, просто возможная слабая гарантия, которая может быть дано их реализацией... пока. - person underscore_d; 25.07.2016
comment
@underscore_d Отображение относится к представлению указателя, определяемому реализацией. В противном случае он хорошо определен. - person D Krueger; 26.07.2016