Что ~~ ("двойная тильда") делает в Javascript?
Сегодня я проверял библиотеку физики онлайн-игр и наткнулся на оператора ~~. Я знаю, что одно ~ является побитовым НЕ, разве это сделало бы ~~ НЕ НЕ, что вернуло бы то же значение, не так ли?
13 ответов
Он удаляет все после десятичной точки, потому что побитовые операторы неявно преобразуют свои операнды в 32-разрядные целые числа со знаком. Это работает независимо от того, являются ли операнды числами (или числами с плавающей точкой), а результатом является число.
Другими словами, это дает:
function(x) {
if(x < 0) return Math.ceil(x);
else return Math.floor(x);
}
только если x находится между - (231) и 231 - 1. В противном случае произойдет переполнение, и число "обернется".
Это может считаться полезным для преобразования строкового аргумента функции в число, но как из-за возможности переполнения, так и из-за того, что оно некорректно для использования с нецелыми числами, я бы не использовал его таким образом, за исключением "кода гольфа" (т.е. бессмысленно обрезать байты исходного кода вашей программы за счет читабельности и надежности). я хотел бы использовать +x
или же Number(x)
вместо.
Как это НЕ из НЕ
Число -43.2, например:
-43,210 = 111111111111111111111111110101012
в виде 32-разрядного двоичного числа со знаком (дополнение к двум). (JavaScript игнорирует то, что находится после десятичной точки.) Инвертирование битов дает:
НЕ -4310 = 000000000000000000000000001010102 = 4210
Инвертирование снова дает:
НЕ 4210 = 111111111111111111111111110101012 = -4310
Это отличается от Math.floor(-43.2)
при этом отрицательные числа округляются до нуля, а не от него. (Функция floor, которая будет равна -44, всегда округляется до следующего более низкого целого числа, независимо от того, является ли число положительным или отрицательным.)
Первый оператор ~ переводит операнд в целое число (возможно, после приведения значения к строке или логическому значению), затем инвертирует младшие 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
В 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);
}
~
кажется делает -(N+1)
, Так ~2 == -(2 + 1) == -3
Если вы сделаете это снова на -3, оно вернется обратно: ~-3 == -(-3 + 1) == 2
Вероятно, он просто конвертирует строку в число в обратном порядке.
Смотрите эту ветку: http://www.sitepoint.com/forums/showthread.php?t=663275
Также более подробная информация доступна здесь: http://dreaminginjavascript.wordpress.com/2008/07/04/28/
Дано ~N
является -(N+1)
, ~~N
затем -(-(N+1) + 1)
, Что, очевидно, приводит к изящному трюку.
Просто предупреждение. Другие ответы привели меня к некоторым неприятностям.
Намерение состоит в том, чтобы удалить что-либо после десятичной точки числа с плавающей запятой, но у него есть несколько угловых случаев, которые делают это ошибкой. Я бы рекомендовал избегать ~~.
Во-первых, ~~ не работает с очень большими числами.
~~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
Преобразование строк в числа
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
}
~~
может использоваться как сокращение для
Math.trunc()
~~8.29 // output 8
Math.trunc(8.29) // output 8
Вот пример того, как этот оператор может использоваться эффективно, где имеет смысл использовать его:
leftOffset = -(~~$('html').css('padding-left').replace('px', '') + ~~$('body').css('margin-left').replace('px', '')),
Источник:
У тильды (~) есть алгоритм -(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
— это два унарных побитовых оператора «нет», расположенных рядом друг с другом.
Это используется как более короткая и обычно более быстрая заменаMath.floor()
для небольших положительных чисел. Поскольку он также выполняет преобразование в числовой шаг, его также можно использовать в качестве замены .~~
даст 0, когдаparseInt
дал бы, например:~~("a1")
дает0
, покаparseInt("a1")
даетNaN
. См. также таблицу преобразования чисел в числа .
Для небольших чисел, которые могут быть отрицательными, это эквивалентноMath.trunc()
: удаляет все, что находится справа от десятичной точки.
Для чисел, абсолютное значение которых больше2 ** 31
(2_147_483_648
) побитовое «не» «переполняется», и его применение дважды дает число, знак и значение которого отключены:
-
~~(2 ** 31)
дает-2_147_483_648
-
~~(2 ** 31 + 1)
дает-2_147_483_647
Чтобы пойти глубже,~
вычисляет побитовое дополнение 32-битной целой части числа. Запустив его дважды, мы получим только 32-битное целое приведение типов. Также см. спецификацию и документацию MDN этого оператора.
Помимо усечения действительных чисел, также может использоваться как оператор для обновления счетчиков в объекте. ~~
примененное к неопределенному объектному свойству, разрешается в ноль и разрешается в то же целое число, если это свойство счетчика уже существует, которое вы затем увеличиваете.
let words=["abc", "a", "b", "b", "bc", "a", "b"];
let wordCounts={};
words.forEach( word => wordCounts[word] = ~~wordCounts[word] + 1 );
console.log("b count == " + wordCounts["b"]); // 3
Следующие два задания эквивалентны.
wordCounts[word] = (wordCounts[word] ? wordCounts[word] : 0) + 1;
wordCounts[word] = ~~wordCounts[word] + 1;
Такой же какMath.abs(Math.trunc(-0.123))
если вы хотите убедиться, что-
также удаляется.