Что такое ^ b и (a & b) << 1?

Я делал этот вопрос в Leetcode.

Запрос:

Вычислите сумму двух целых чисел a и b, но вы не можете использовать операторы + и -.

Я не могу понять решение, которое оно дало

Может кто-нибудь объяснить, как работает эта функция getSum?

Вот ответ в JS:

var getSum=function(a,b) {
    const Sum = a^b; //I can't understand how those two line code can
    const carry = (a & b) << 1; //get the sum
        if(!carry) {
            return Sum
        }
    return getSum(Sum,carry);
};
console.log(getSum(5,1));

Ответы

Ответ 1

Давайте учиться на собственном примере. Представьте, что a = 3 и b = 5

В двоичной записи они a = 0011 и b = 0101

XOR: a^b является оператором XOR. При сравнении двух битов возвращается 0 если они одинаковы, и 1 если они разные. 01^10 => 11

Поэтому, когда мы делаем a^b результат будет 0110.

И + SHIFT

a&b выполняет логическую операцию И Возвращает 1 только тогда, когда a = b = 1.

В нашем случае результат 0001

<< сдвигает его (добавляет 0 на правой стороне), и результат стал 0010, который устанавливает carry переменной верно. (только 0000 будет ложным).

Следующие итерации:

Все повторяется, но теперь a = 0110 и b = 0010 (Sum и carry с последнего выполнения)

Теперь a^b = 0100 и (a&b)<<1 = 0100

Повторяю еще раз.

Теперь a^b = 0000 и (a&b)<<1 = 1000

И опять.

Теперь a^b = 1000 и (a&b)<<1 = 0000. Теперь carry, наконец, false. И мы возвращаем 1000 что является десятичным 8.

Все работало нормально, так как 3+5=8

Ответ 2

Это в основном тиражирование полусуммера

Добавление 2 битов A и B приводит к 2 выходам: сумма и бит переноса, как показано ниже

╔═══════╤═════════════╗
║ Input │   Output    ║
╠═══╤═══╪═══════╤═════╣
║ A │ B │ carry │ sum ║
╟───┼───┼───────┼─────╢
║ 0 │ 0 │   0   │  0  ║
╟───┼───┼───────┼─────╢
║ 1 │ 0 │   0   │  1  ║
╟───┼───┼───────┼─────╢
║ 0 │ 1 │   0   │  1  ║
╟───┼───┼───────┼─────╢
║ 1 │ 1 │   1   │  0  ║
╚═══╧═══╧═══════╧═════╝

Из таблицы мы получаем логику для выходов: carry = A и B, sum = A xor B

b7rLk.png

XOR также называется оператором сложения без переноса и представлен символом ⊕ с символом + внутри

Таким образом, фрагмент выше работает так

const Sum=a^b;              // sum = a xor b = a ⊕ b
const carry=(a&b)<<1;       // carry = 2*(a and b), since we carry to the next bit
if(!carry){
    return Sum;             // no carry, so sum + carry = sum
}
return getSum(Sum,carry);   // a + b = sum + carry

Таким образом, a^b добавляет каждый бит в и b одновременно, оставляя не переносимую сумму a и b в Sum. Затем мы должны добавить перенос к сумме без переноса, чтобы получить окончательный результат, так как у нас есть только полусуммер вместо полного сумматора, который выполняет перенос a + b = a ⊕ b +

Смотрите также

Ответ 3

 int result = p ^ q; // XOR Operator, + without carry 0+0=0, 0+1=1+0=1, 1+1=0
int carry = (p & q) << 1; // Left Shift, 1+1=2
if (carry != 0) {
    return getSum(result, carry);
}
return result;

Начните с p = 5, q = 6. Тогда XOR будет,

0101
0110
------
0011

Таким образом, XORing приводит к (0011), который на самом деле 3 в десятичном виде. Тогда ANDing p и q мы получаем,

0101
0110
-------
0100

Мы получаем 4 (100 в двоичном виде) с помощью ANDing 5 & 6, теперь, если мы сместим это значение влево, мы получим

 0100<<1=1000

Таким образом, мы получаем 8 (1000 в двоичном виде) после первой рекурсии. Так как результат (переменная переноса) не равен нулю, давайте повторим рекурсию по значению xor и значению переноса.

getSum(3, 8);

Итак, делая первый XORing, мы получаем,

0011
1000
-------
1011

XORing на этот раз дал 11 (1011 двоичный), поэтому мы выполняем И сейчас,

0011
1000
-------
0000

Мы получаем все НУЛИ для ANDing 3 и 8, поэтому на этот раз оператор сдвига влево также приводит к нулю, так как здесь у нас нет 1, который может дать нам значение путем смещения нуля влево. Поскольку переменная переноса теперь равна нулю, мы заканчиваем рекурсию, а значением XORed будет сумма, равная 11 (1011 в двоичном виде).

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

Ответ 4

^ - это XOR, побитовая операция. Для одного бита применяются следующие правила: 0 ^ 0 = 0, 0 ^ 1 = 1, 1 ^ 0 = 0 и 1 ^ 1 = 0, и вы просто расширяете его для соответствующих битов при работе с многобитовыми значениями. Название сокращенно от "исключающее или" и происходит от того факта, что A ^ B равно 1 если и только если A или B равно 1, а не оба. Но более интересно поговорить о другом его названии ⊕. ⊕ +, но немного отличается. Вы заметите, что правила для ⊕ аналогичны правилам сложения: 0 + 0 = 0, 0 + 1 = 1, 1 + 0 = 1 и 1 + 1 = 10. ⊕ равно +, кроме 1 ⊕ 1 = 0; то есть ⊕ +, за исключением случаев, когда нет. Это верно для нескольких битов: 011 + 001 = 100, потому что вы переносите 1 из одного места в место двойки, а затем снова переносите 1 в место четверки. Тогда 011 ⊕ 001 = 010, потому что вы просто не несете.

Теперь, когда вы делаете настоящее дополнение, когда вы носите? В двоичном коде ответ очень прост: вы переносите 1 на следующее место, когда в данном месте есть два 1. Это легко понять как побитовое И, &. 1 & 1 = 1 и 0 противном случае. Для 011 + 001 сложение без переноса дает 011 ⊕ 001 = 010, и мы можем сказать, что нам нужно вынести 1 из одного места, потому что 011 & 001 = 001. Сдвиг в (a & b) << 1 превращает число "откуда мне нужно нести?" в "где мне нужно добавить переносов?": (011 & 001) << 1 = 010; Мне нужно добавить немного переноса в месте двойки.

Итак, в getSum мы хотим знать a + b. Мы вычисляем сложение без переноса с a ^ b и находим, где нам нужно добавить переносимые биты с (a & b) << 1. Теперь нам просто нужно сложить эти два. Ну, у нас уже есть функция для сложения чисел; это называется getSum. Итак, мы просто пишем function getSum(a, b) { return getSum(a ^ b, (a & b) << 1); } function getSum(a, b) { return getSum(a ^ b, (a & b) << 1); }, за исключением того, что мы обеспечиваем короткое замыкание, если нечего нести, спасая нас от бесконечной рекурсии.