Я смотрю на некоторый код C ++, и я вижу:
byte b = someByteValue;
// take twos complement
byte TwosComplement = -b;
Этот код принимает двойное дополнение b? Если нет, что он делает?
это код определенно вычисляет двойное дополнение 8-битного двоичного числа в любой реализации, где stdint.h
определяет uint8_t
:
#include <stdint.h>
uint8_t twos_complement(uint8_t val)
{
return -(unsigned int)val;
}
Это потому, если uint8_t
доступно, это должен быть тип без знака, который имеет ширину ровно 8 бит. Преобразование в unsigned int
необходимо, потому что uint8_t
определенно уже int
, Без преобразования значение будет повышено до int
до того, как он будет отменен, поэтому, если вы работаете на машине, не являющейся дополнением к двойкам, она не будет принимать дополнения к двум.
В более общем смысле этот код вычисляет двойное дополнение значения с любой тип без знака (с использованием конструкций C ++ для иллюстрации — поведение унарного минуса одинаково в обоих языках, при условии отсутствия пользовательских перегрузок):
#include <cstdint>
#include <type_traits>
template <typename T>
T twos_complement(T val,
// "allow this template to be instantiated only for unsigned types"typename std::enable_if<std::is_unsigned<T>::value>::type* = 0)
{
return -std::uintmax_t(val);
}
потому что унарный минус определен так, чтобы принимать двойное дополнение применительно к неподписанным типам. Нам все еще нужен приведение к типу без знака, который не является более узким, чем int
, но теперь нам нужно, чтобы он был как минимум настолько широким, насколько это возможно T
отсюда uintmax_t
,
Тем не менее, унарный минус делает не обязательно вычислить двойное дополнение значения, тип которого подписанный, потому что C (и C ++) все еще явно разрешают реализации, основанные на процессорах, которые не используйте два дополнения для подписанных количеств. Насколько я знаю, ни один такой процессор не производился по крайней мере в течение 20 лет, поэтому постоянное обеспечение для них довольно глупо, но это так. Если вы хотите вычислить двойное дополнение значения, даже если его тип подписан, вы должны сделать это: (снова C ++)
#include <type_traits>
template <typename T>
T twos_complement(T val)
{
typedef std::make_unsigned<T>::type U;
return T(-uintmax_t(U(val)));
}
то есть преобразовать в соответствующий тип без знака, затем в uintmax_t
, затем примените унарный минус, затем выполните обратное преобразование в возможно подписанный тип. (Приведение к U требуется, чтобы убедиться, что значение равно нулю, а не расширено от его естественной ширины.)
(Однако, если вы обнаружите, что делаете это, остановите и вместо этого поменяйте типы на unsigned. Ваша будущая личность поблагодарит вас.)
Правильное выражение будет выглядеть
byte TwosComplement = ~b + 1;
Примечание: при условии, что байт i определен как unsigned char
На машине дополнения до двух отрицание вычисляет дополнение до двух, да.
На Unisys что-то, что-то, возможно, теперь мертвое и похороненное (но все еще существующее несколько лет назад), нет для подписанного типа.
C и C ++ поддерживают два дополнения, одно дополнение и представление знака и величины целых чисел со знаком, и только с дополнением два отрицание делает дополнение двух.
С byte
как отрицание типа без знака плюс преобразование в byte
генерирует битовый шаблон дополнения до двух, независимо от целочисленного представления, потому что преобразование в арифметику без знака и без знака происходит по модулю 2N где N количество бит представления значения
Таким образом, результирующее значение после присвоения или инициализации с -x равно 2N — х, который является дополнением к двум х.
Это не означает, что само отрицание обязательно вычисляет битовый шаблон дополнения двух. Чтобы понять это, обратите внимание, что с byte
определяется как unsigned char
, и с sizeof(int)
> 1, byte
значение повышается до int
перед отрицанием, то есть операция отрицания выполняется со знаком типа. Но преобразование полученного отрицательного значения в беззнаковый байт создает двоичный набор дополнений по определению и гарантию C ++ по модулю арифметики и преобразованию в беззнаковый тип.
Полезность формы дополнения 2 следует из 2N — х = 1 + ((2N — 1) — x), где последняя круглая скобка представляет собой единичный битпаттерн минус Икс, т.е. простая побитовая инверсия x
,