Что ~~ ("двойная тильда") делает в Javascript? - PullRequest
192 голосов
/ 30 октября 2010

Сегодня я проверял библиотеку физики онлайн-игр и наткнулся на оператора ~~. Я знаю, что одиночный ~ является побитовым НЕ, разве это сделало бы ~~ НЕ НЕ, что вернуло бы то же значение, не так ли?

Ответы [ 9 ]

235 голосов
/ 30 октября 2010

Он удаляет все после десятичной точки, потому что побитовые операторы неявно преобразуют свои операнды в 32-разрядные целые числа со знаком. Это работает независимо от того, являются ли операнды числами (или числами с плавающей точкой), и результатом является число.

Другими словами, это дает:

function(x) {
  if(x < 0) return Math.ceil(x);
  else return Math.floor(x);
}

только в том случае, если x находится между - (2 31 ) и 2 31 - 1. В противном случае произойдет переполнение, и число будет "округлено" .

Это может считаться полезным для преобразования строкового аргумента функции в число, но как из-за возможности переполнения, так и из-за того, что оно некорректно для использования с нецелыми числами, я бы не использовал его таким образом, за исключением «кода гольфа» "( т.е. бессмысленно обрезает байты исходного кода вашей программы за счет читабельности и надежности). Я бы использовал +x или Number(x) вместо.


Как это НЕ из НЕ

Число -43,2, например:

-43,2 10 = 11111111111111111111111111010101 2

как 32-разрядное двоичное число со знаком (дополнение к двум). (JavaScript игнорирует то, что находится после десятичной точки.) Инвертирование битов дает:

НЕ -43 10 = 00000000000000000000000000101010 2 = 42 10

Инвертирование снова дает:

НЕ 42 10 = 11111111111111111111111111010101 2 = -43 10

Это отличается от Math.floor(-43.2) тем, что отрицательные числа округляются до нуля, а не от него. (Функция floor, которая будет равна -44, всегда округляется до следующего нижнего целого числа, независимо от того, является ли число положительным или отрицательным.)

30 голосов
/ 30 октября 2010

Первый оператор ~ переводит операнд в целое число (возможно, после приведения значения к строке или логическому значению), а затем инвертирует младшие 31 бит. Официально все числа ECMAScript являются числами с плавающей точкой, но некоторые числа реализованы как 31-разрядные целые числа в движке SpiderMonkey.

Вы можете использовать его, чтобы превратить массив из 1 элемента в целое число. Плавающие точки конвертируются в соответствии с правилом C, т.е. усечение дробной части.

Затем второй оператор ~ инвертирует биты обратно, так что вы знаете, что у вас будет целое число. Это не то же самое, что принудительное приведение значения к логическому значению в операторе условия, поскольку пустой объект {} оценивается как true, тогда как ~~ {} оценивается как false.

js>~~"yes"
0
js>~~3
3
js>~~"yes"
0
js>~~false
0
js>~~""
0
js>~~true
1
js>~~"3"
3
js>~~{}
0
js>~~{a:2}
0
js>~~[2]
2
js>~~[2,3]
0
js>~~{toString: function() {return 4}}
4
js>~~NaN
0
js>~~[4.5]
4
js>~~5.6
5
js>~~-5.6
-5
18 голосов
/ 17 декабря 2014

В ECMAScript 6 эквивалент ~~ равен Math.trunc :

Возвращает неотъемлемую часть числа, удаляя все дробные цифры. Он не округляет никаких чисел.

Math.trunc(13.37)   // 13
Math.trunc(42.84)   // 42
Math.trunc(0.123)   //  0
Math.trunc(-0.123)  // -0
Math.trunc("-1.123")// -1
Math.trunc(NaN)     // NaN
Math.trunc("foo")   // NaN
Math.trunc()        // NaN

Полифилл:

function trunc(x) {
    return x < 0 ? Math.ceil(x) : Math.floor(x);
}
8 голосов
/ 30 октября 2010

Если ~N равно -(N+1), то ~~N равно -(-(N+1) + 1).Что, очевидно, приводит к аккуратной уловке .

2 голосов
/ 29 августа 2016

Просто предупреждение.Другие ответы здесь привели меня к некоторой проблеме.

Намерение состоит в том, чтобы удалить что-либо после десятичной точки числа с плавающей запятой, но у него есть некоторые угловые случаи, которые делают это ошибкой.Я бы рекомендовал избегать ~~.

Во-первых, ~~ не работает с очень большими числами.

~~1000000000000 == -727279968

В качестве альтернативы используйте Math.trunc() (как уже упоминал Гаджус, Math.trunc() возвращает целую часть числа с плавающей запятой, но доступно только в JavaScript, совместимом с ECMAScript 6),Вы всегда можете сделать свой собственный Math.trunc() для не-ECMAScript-6 сред, выполнив это:

if(!Math.trunc){
    Math.trunc = function(value){
        return Math.sign(value) * Math.floor(Math.abs(value));
    }
}

Я написал пост в блоге для справки: http://bitlords.blogspot.com/2016/08/the-double-tilde-x-technique-in.html

1 голос
/ 26 декабря 2016

Преобразование строк в числа

console.log(~~-1);    // -1
console.log(~~0);     // 0
console.log(~~1);     // 1
console.log(~~"-1");  // -1
console.log(~~"0");   // 0
console.log(~~"1");   // 1
console.log(~~true);  // 1
console.log(~~false); // 0

~ -1 равно 0

if (~someStr.indexOf("a")) {
  // Found it
} else  {
  // Not Found
}

источник

1 голос
/ 19 июля 2015

Вот пример того, как этот оператор может эффективно использоваться, где имеет смысл его использовать:

leftOffset = -(~~$('html').css('padding-left').replace('px', '') + ~~$('body').css('margin-left').replace('px', '')),

Источник:

См. Раздел Взаимодействие с точками

0 голосов
/ 13 октября 2016

Тильда (~) имеет алгоритм - (N + 1)

Например:

~0 = -(0+1) = -1
~5 = -(5+1) = -6
~-7 = -(-7+1) = 6

Двойная тильда - - (- (N + 1) +1)

Например:

~~5 = -(-(5+1)+1) = 5
~~-3 = -(-(-3+1)+1) = -3

Тройная тильда - - (- (- (N + 1) +1) +1)

Например:

~~~2 = -(-(-(2+1)+1)+1) = -3
~~~3 = -(-(-(3+1)+1)+1) = -4
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...