Есть код:
for(int i = -2; i<2; i++)
System.out.printf("Инверсия %d даст %d. \n", i, ~i );
На выходе:
Инверсия -2 даст 1.
Инверсия -1 даст 0.
Инверсия 0 даст -1.
Инверсия 1 даст -2.
Оператор инверсии похоже реализован так:
Operator ~ (int i){
return -i-1;
}
Но пишут, что инверсия инвертирует нули в единицы. Хорошо, для -2 (инт для упрощения 4 бита берём) 1010, первый бит - знак минус. Значит инверсия будет 0101, то есть 5. Дополнительный код: 0101 + 1 = 0110 - итого 6. Короче, никак у меня в теории не удаётся инвертировать код
Вопрос: Как на самом деле (объясните на единицах и нулях) работает этот проклятый оператор? И зачем разработчики языка сделали его работу именно такой?
Инверсия является побитовой операцией и преобразует хранящиеся в памяти единицы в нули и нули в единицы. Чтобы понять почему она так работает с типом int нужно изучит структуру представления примитивного типа int в памяти. Примитивный тип int состоит из четырех байт. Старший бит старшего байта отвечает за знак числа, остальные - за значение. Отрицательные числа хранятся в дополнительном коде.
Разберем пример для числа -2:
System.out.println(Integer.toBinaryString(-2));
//11111111 11111111 11111111 11111110 - битовое представление числа -2
после операции побитовой инверсии мы получим: 00000000 00000000 00000000 00000001, что эквивалентно десятичной 1 Таким образом для корректного использования побитовых операций важно знать внутреннюю структуру обрабатываемых данных и при необходимости работать только с нужной частью битов используя маски и сдвиги. Необходимо учитывать и то, что другие целочисленные типы при многих операциях так же приводятся к типу int.
Отрицательные числа компьютер хранит в дополнительном коде. С точки зрения математики, дополнительный код - это кольцо вычетов по модулю 2N. Не пугайтесь, это страшное слово означает всего лишь, что к отрицательным числам добавляется 2N, где N - число разрядов.
К примеру, 32х-битное число -1 в памяти хранится как 232-1 = 4294967295. Или, в двоичном виде, 11111111 11111111 11111111 111111112.
Аналогично, число -2 в памяти хранится как 232-2 = 11111111 11111111 11111111 111111102, а число -3 - как 232-3 = 11111111 11111111 11111111 111111012.
Легко видеть, что инверсия одного бита с точки зрения математики - это вычитание его из 1: 1-1=0, а 1-0=1. Инверсия же всех 32х бит числа - это вычитание их его из 32х единиц. Но, как уже было показано выше, число состоящее из 32х единиц - это -1.
Таким образом, математически вы правы, оператор инверсии действительно работает так как вы написали:
~x = -1-x
Но не следует думать что эта формула - его реализация. Это всего лишь его математическое свойство, а реализован он по определению - как инверсия всех бит числа.
Кстати, если перенести единицу в другую часть, получится более интересная формула:
~x + 1 = -x
Интересна эта формула тем, что является реализацией оператора "унарный минус" в процессоре. Именно так процессоры вычитают целые числа:
x - y = x + ~y + 1
Сборка персонального компьютера от Artline: умный выбор для современных пользователей