k12th
@k12th
console.log(`You're pulling my leg, right?`);

Битовое отрицание в JS

Последнее время на собеседованиях попадаются задачки такого вида:
~~3.14 // 3
или
~0 // -1
и у первой, и у второй ответ довольно неожиданный.
Вопрос, почему? Из того, что я знаю о представлении чисел с плавающей точкой в памяти компьютера… второе еще можно как-то понять, но первое?
  • Вопрос задан
  • 4947 просмотров
Решения вопроса 1
Weageoo
@Weageoo
Всё просто: побитовые операторы в js работают только со знаковыми (signed) целыми (integer) длиной в 32 бита (оператор >>> работает с unsigned). Иными словами, побитовые операторы интерпретируют операнды как последовательность из 32 битов.

Т.о., при выполнении ~3 число 3 представляется в виде 0000 0000 0000 0000 0000 0000 0000 0011, результат, понятное дело 1111 1111 1111 1111 1111 1111 1111 1100 — это -4. Если крайний левый (знаковый) бит равен 0 — число положительное, если равен 1 — число отрицательное. Максимальное знаковое целое, которое уместится в 32 бита — это 0111 1111 1111 1111 1111 1111 1111 1111, или 2147483647 (по основанию 10). Минимальное 32-bit signed integer — это 1000 0000 0000 0000 0000 0000 0000 0000, или -2147483648.

2147483647   0111 1111 1111 1111 1111 1111 1111 1111
100          0000 0000 0000 0000 0000 0000 0110 0100
5            0000 0000 0000 0000 0000 0000 0000 0101
4            0000 0000 0000 0000 0000 0000 0000 0100
3            0000 0000 0000 0000 0000 0000 0000 0011
2            0000 0000 0000 0000 0000 0000 0000 0010
1            0000 0000 0000 0000 0000 0000 0000 0001
0            0000 0000 0000 0000 0000 0000 0000 0000
-1           1111 1111 1111 1111 1111 1111 1111 1111
-2           1111 1111 1111 1111 1111 1111 1111 1110
-3           1111 1111 1111 1111 1111 1111 1111 1101
-4           1111 1111 1111 1111 1111 1111 1111 1100
-5           1111 1111 1111 1111 1111 1111 1111 1011
-100         1111 1111 1111 1111 1111 1111 1001 1011
-2147483648  1000 0000 0000 0000 0000 0000 0000 0000

~~3.14 // 3

Шаг 1: число приводится к целому и представляется в виде последовательности 0000 0000 0000 0000 0000 0000 0000 0011.
Шаг 2: производится сама побитовая замена 0->1 и 1->0. Получаем 1111 1111 1111 1111 1111 1111 1111 1100, или -4 (по осн. 10).
Шаг 3: производится ~(-4), то бишь получим в итоге 3.

Ну, с нулем всё понятно думаю.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
При битовом отрицании число приводится к целому типу.
Ответ написан
RubaXa
@RubaXa
Для простоты понимания, это просто: ~X === (-1 — X)
Ответ написан
Комментировать
sompylasar
@sompylasar
Frontend Software Engineer
На собеседованиях спрашивают потому, что ~~ — это быстрая альтернатива Math.floor для небольших чисел (которые влезают в int32).

jsperf.com/math-floor-vs-math-round-vs-parseint/74

Для выполнения Math.floor движку потребуется пройтись вверх до глобального пространства имен, чтобы найти там Math (вдруг кто определил переменную с таким именем?), получить свойство floor объекта (вдруг кто перезаписал его?), вызвать его как функцию, в которую передать параметр, затем вернуть значение.

В случае с оператором, который в JavaScript перегрузить нельзя, накладные расходы минимальны.

Единственное ограничение — разрядность. С современными timestamp'ами из Date#getTime, к сожалению, уже не сработает (будет переполнение).
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
19 апр. 2024, в 23:00
5000 руб./за проект
19 апр. 2024, в 20:43
20000 руб./за проект