Как преобразовать дополнение 2 в его соответствующий знаковый аналог, естественный для машины?

Пусть a будет переменной целочисленного типа со знаком T, а U - соответствующим беззнаковым типом. Выражение (U)a дает значение, соответствующее представлению в дополнении до двух значения a как U. Я хочу знать, гарантирует ли стандарт C следующее, чтобы отменить это приведение. Быть u типа U и иметь значение (U)a. Be MAX максимальное значение, которое может содержать тип T. (Помните о неявных преобразованиях в типы без знака и о том факте, что каждое положительное значение переменной со знаком остается неизменным при этих преобразованиях.)

Во-первых, предположим, что T может хранить результат:

T convert_2scomplement_to_T(U n) {
    return n<=MAX ? n : -(T)(U)-n;
}

Во-вторых, допустим, функция должна обнаружить такой недопустимый аргумент; быть MIN минимальное значение T может содержать:

T convert_2scomplement_to_T_checked(U n) {
    if(n <= MAX) return n;
    if( !(n & (U)1 << sizeof(U)*CHAR_BIT-1) ) { // (*)
        // invalid argument, the value is positive and `T' cannot hold it
    }
    /* `n' represents something negative if we're here. */
    if(-n < MIN) {
        // invalid argument, the value is negative and `T' cannot hold it
    }
    return -(T)(U)-n;
}

Строка, отмеченная // (*), насколько я могу судить, не является строго соответствующей, потому что стандарт не дает никаких гарантий относительно положения бита знака.

Описанные функции работают должным образом? И можно ли избежать проверки бита знака в строго соответствующем коде?

(И помимо языкового юриста… было бы здорово, если бы кто-то, кто написал код, зная, что хотя бы один человек использовал его на платформе, не использующей дополнение до двух, мог бы оставить комментарий, что это была за машина. Википедия упоминает

величина со знаком:

дополнение:

но, похоже, в программах, написанных сегодня, об этом не стоит беспокоиться. Есть ли причина, по которой стандарт все еще касается таких машин?)


person mafso    schedule 03.07.2014    source источник
comment
Вопрос кажется неясным; если вы просите что-то, что работает только на машинах с двумя комплементами?   -  person M.M    schedule 03.07.2014
comment
Выражение (U)a дает значение, соответствующее представлению дополнения до двух значения a - в общем случае неверно. Если вы спрашиваете об обратной операции (U)a в целом, просто укажите, что вместо добавления 2 к 2   -  person M.M    schedule 03.07.2014
comment
@MattMcNabb: Нет, я прошу строго соответствующее решение.   -  person mafso    schedule 03.07.2014
comment
Вы можете использовать союз, чтобы напечатать каламбур целого числа со знаком и без знака одного размера. C99 явно разрешает каламбур объединения.   -  person Mysticial    schedule 03.07.2014
comment
@Mysticial: я не хочу каламбурить. Я хочу отменить приведение (они эквивалентны на машинах с 2-м дополнением, поэтому я не могу попробовать свой код, потому что у меня нет доступа к машине без 2-го дополнения).   -  person mafso    schedule 03.07.2014
comment
@MattMcNabb: Да, в целом это правда. Стандарт описывает это как «[…] значение преобразуется путем многократного добавления или вычитания на единицу больше, чем максимальное значение, которое может быть представлено в новом типе, до тех пор, пока значение не окажется в диапазоне нового типа» (C99/C11 6.3). .1.3 p.2), что фактически является дополнением до 2.   -  person mafso    schedule 03.07.2014
comment
Я не согласен. Каково представление дополнения 2 для -1? Вы не можете ответить на этот вопрос, не зная ширины типа. Допустим, int имеет 1 бит знака, 15 битов значения и 16 бит заполнения; а unsigned int имеет 32 бита значения. Тогда дополнение до двух для -1 будет 0xFFFF, но (U)a будет 0xFFFFFFFF. Итак, на самом деле вы имеете в виду представление в дополнении 2 значения a, но где используемая ширина - это ширина U. Это кажется ненужным отвлечением, когда вы можете просто написать (U)a, что совершенно однозначно.   -  person M.M    schedule 03.07.2014
comment
Ваш вопрос Как мне закодировать T func(U) так, чтобы t == func(t) для всех t?, или что-то еще.   -  person M.M    schedule 03.07.2014
comment
@MattMcNabb: Да, это именно то, о чем я прошу.   -  person mafso    schedule 03.07.2014
comment
@MattMcNabb: И да, я не знал, что неподписанные типы также могут иметь биты заполнения (как-то я думал, что их не будет). Если вы думаете, вы могли бы переформулировать вопрос, чтобы сделать его более ясным, было бы здорово, если бы вы это сделали (я думаю, у вас достаточно представителей), я на самом деле не знаю, где я был неясен.   -  person mafso    schedule 03.07.2014
comment
Вероятно, вам также необходимо включить предположение об отсутствии битов заполнения   -  person M.M    schedule 03.07.2014
comment
Unsigned не может иметь биты заполнения   -  person M.M    schedule 03.07.2014
comment
@MattMcNabb: C99/C11 6.2.6.2 п.1, на мой взгляд, прямо указывает на обратное: у них могут быть биты заполнения (если это не unsigned char).   -  person mafso    schedule 03.07.2014
comment
@MattMcNabb: я не хочу добавлять такое предположение, я хочу оставаться в строгом соответствии. Все это вопрос языкового юриста, я думаю, я никогда не буду писать код, который должен быть действительным на машине, не использующей дополнение до двух.   -  person mafso    schedule 03.07.2014
comment
@mafso хорошо. Я думал, что другие разделы на самом деле означают, что неподписанные биты заполнения не могут соответствовать, но сейчас я не могу вспомнить, что это были за другие разделы.   -  person M.M    schedule 03.07.2014
comment
@MattMcNabb: Так я и думал в первую очередь, именно ваш комментарий заставил меня изучить стандарт по этому поводу, и я пришел к выводу, что заполнение допустимо для (не-char) целочисленных типов без знака. Я тоже не знаю, есть ли соответствующие части стандарта, дающие больше ограничений на беззнаковые представления…   -  person mafso    schedule 03.07.2014
comment
Если вы пытаетесь преобразовать два дополнения обратно в число со знаком, вы можете увидеть главный ответ (от hvd) на этот вопрос: stackoverflow.com/questions/13150449/ Ответ был на С++, но я думаю это относится и к С.   -  person qbt937    schedule 28.12.2014
comment
@qbt937; Спасибо, не нашел такой. Проголосовал за закрытие моего вопроса как дубликата.   -  person mafso    schedule 29.12.2014
comment
@mafso Вы можете попробовать еще раз, чтобы закрыть его.   -  person Artjom B.    schedule 03.04.2015


Ответы (2)


Я не думаю, что это сложно. Арифметика дополнения на самом деле проще, потому что каждое число является дополнением своего отрицательного значения. Единственная проблема связана со всеми установленными битами, потому что это переводится как отрицательный ноль.

Так что да, приведение S->U->S определенно обратимо, но U->S->U может не работать для значений с установленным старшим битом. И вы, возможно, не сможете отменить это для отрицательного нуля, потому что информация может быть потеряна.

Приведение знакового к беззнаковому сохраняет битовый шаблон и не является операцией.

Unsigned to signed имеет дело с числами, у которых установлен старший бит. Все включенные биты являются допустимыми и представляют собой отрицательный нуль, и их можно перехватывать, обрабатывать как ноль или принудительно обнулять (по крайней мере, это мое чтение, но у меня нет под рукой Univac 1100, чтобы попробовать его). Другие значения с установленным старшим битом находятся за пределами диапазона со знаком, поэтому поведение определяется реализацией. Скорее всего, это сохранит битовый рисунок, как это проще всего сделать.

Если вы ищете гарантии, вы можете их не найти. Большая часть C/C++ такова.

person david.pfx    schedule 08.07.2014
comment
«Приведение знакового к беззнаковому сохраняет битовый шаблон и является неоперативным». — Нет, это неоперативно только на машинах, использующих дополнение до 2. Если нет, это преобразование в дополнение до 2 (игнорируя биты заполнения). То есть, если n имеет тип int, то (unsigned)n имеет значение, отличное от *(unsigned *)&n, если и только если n отрицательное и у нас есть машина, не использующая дополнение до 2. - person mafso; 09.07.2014
comment
Возможно, мой вопрос был немного неясен: в качестве примера (давайте на мгновение проигнорируем биты заполнения), подумайте о программе, получающей целые числа без знака в качестве входных данных, представляющих дополнение 2 определенного значения, и мы хотим сохранить это значение в соответствующем целочисленном объекте со знаком (я не хочу набирать каламбур/переосмысливать, я хочу конвертировать). - person mafso; 09.07.2014
comment
Проведя некоторые дополнительные исследования, я написал совершенно новый ответ. Надеюсь, вам понравится. - person david.pfx; 09.07.2014

Интегральные преобразования для дополнения до двух определяются следующим образом. n3936 S4.7/2,3 [аналог n1570 s6.3.1.3]

Если целевой тип не имеет знака, результирующее значение является наименьшим целым числом без знака, сравнимым с исходным целым числом (по модулю 2n, где n — количество битов, используемых для представления беззнакового типа). [Примечание: в представлении с дополнением до двух это преобразование является концептуальным, и битовый шаблон не изменяется (если нет усечения). -конец примечания]

Конгруэнтность определяется: (Математика) отношение между двумя числами, показывающее, что числа дают одинаковый остаток при делении на некоторое заданное число.

Наименьший означает наименьший или наименьший по значению. «Некоторое заданное значение» равно 2 ^ количеству бит.

Итак, преобразование без знака оценивает (k mod 2^n), что равно (2^n - k).

  • с четырехбитным числом (со знаком)-1 => (без знака)15
  • с 16-битным числом (со знаком)-1 => (без знака)65535
  • с 32-битным числом (со знаком)-1 => (без знака)4294967295

В каждом случае это требует копирования битовой комбинации и увеличения результата.

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

В каждом из этих случаев (беззнаковое) значение не может быть представлено в (со знаком) целевом типе, поэтому вам придется проверить определения реализации. Продуманная реализация могла бы отменить указанную выше операцию, но стандарт этого не требует.

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

person david.pfx    schedule 09.07.2014
comment
Большое спасибо за потраченное на это время! Сегодня посмотрю (сейчас времени нет). - person mafso; 09.07.2014