Самая частая идиома из всех. Число чётно тогда и только тогда, когда его младший бит равен нулю – мы отмечали это ещё в уроке 6.1, теперь проговорим до конца.
Вспомните веса разрядов из урока 2.4: значение числа – это сумма битов, умноженных на веса 1, 2, 4, 8, 16… Все веса, кроме нулевого, чётные, значит и вклад их чётный при любых битах. Чётность всей суммы решает единственный разряд – нулевой.
x & 1равно 1 у нечётных чисел и 0 у чётных.
Проверяем: 6 & 1 → 0, 7 & 1 → 1, 156 & 1 → 0.
Отдельно про отрицательные, потому что тут Python ведёт себя удобнее многих языков: -7 & 1 даёт 1, и -7 % 2 тоже даёт 1 – ответы совпадают. В C и Java -7 % 2 равно −1, и там & 1 и % 2 – уже разные проверки. Знакомая история: с приоритетом & и == из урока 6.1 было ровно так же.
А теперь обобщение, которое прямо следует из маски младших n битов (урок 6.3). Остаток от деления на степень двойки – это тоже маска:
x % (2 ** n)– это то же самое, чтоx & ((1 << n) - 1).
То есть x % 8 – это x & 7, а x % 256 – это x & 0xFF. В Python равенство выполняется для любых целых, включая отрицательные: -9 & 7 и -9 % 8 оба дают 7. Причина та же, что и с чётностью, – остаток по положительному модулю в Python неотрицателен, и младшие биты дополнительного кода дают ровно его.
Вместе с правилом из урока 6.2 получается полная пара: x >> n – частное от деления на 2 ** n, x & ((1 << n) - 1) – остаток.