Пусть 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;
}
Строка, отмеченная // (*), насколько я могу судить, не является строго соответствующей, потому что стандарт не дает никаких гарантий относительно положения бита знака.
Описанные функции работают должным образом? И можно ли избежать проверки бита знака в строго соответствующем коде?
(И помимо языкового юриста... было бы здорово, если бы кто-то, кто написал код, зная, что хотя бы один человек использовал его на платформе, не использующей дополнение до двух, мог бы оставить комментарий, что это была за машина. Википедия упоминает
величина со знаком:
дополнение:
но, похоже, в программах, написанных сегодня, об этом не стоит беспокоиться. Есть ли причина, по которой стандарт все еще касается таких машин?)
Ответ 1 · david.pfx
Я не думаю, что это сложно. Арифметика дополнения на самом деле проще, потому что каждое число является дополнением своего отрицательного значения. Единственная проблема связана со всеми установленными битами, потому что это переводится как отрицательный ноль.
Так что да, приведение S->U->S определенно обратимо, но U->S->U может не работать для значений с установленным старшим битом. И вы, возможно, не сможете отменить это для отрицательного нуля, потому что информация может быть потеряна.
Приведение знакового к беззнаковому сохраняет битовый шаблон и не является операцией.
Unsigned to signed имеет дело с числами, у которых установлен старший бит. Все включенные биты являются допустимыми и представляют собой отрицательный нуль, и их можно перехватывать, обрабатывать как ноль или принудительно обнулять (по крайней мере, это мое чтение, но у меня нет под рукой Univac 1100, чтобы попробовать его). Другие значения с установленным старшим битом находятся за пределами диапазона со знаком, поэтому поведение определяется реализацией. Скорее всего, это сохранит битовый рисунок, как это проще всего сделать.
Если вы ищете гарантии, вы можете их не найти. Большая часть C/C++ такова.
Ответ 2 · david.pfx
Интегральные преобразования для дополнения до двух определяются следующим образом. 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
В каждом случае это требует копирования битовой комбинации и увеличения результата.
Если целевой тип знаковый, значение не изменяется, если оно может быть представлено в целевом типе (и ширине битового поля); в противном случае значение определяется реализацией.
В каждом из этих случаев (беззнаковое) значение не может быть представлено в (со знаком) целевом типе, поэтому вам придется проверить определения реализации. Продуманная реализация могла бы отменить указанную выше операцию, но стандарт этого не требует.
Таким образом, ответ заключается в том, что стандарт не гарантирует циклическое преобразование целых чисел со знаком в беззнаковое для реализации дополнения до двух.