Що робить ~~ ("подвійний тильд") у Javascript?


205

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


Відповіді:


248

Він видаляє все після десяткової крапки, оскільки побітові оператори неявно перетворюють свої операнди в підписані 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)тим, що від’ємні числа округляються до нуля, а не від нього. (Функція підлоги, яка дорівнювала б -44, завжди округляється до наступного нижнього цілого числа, незалежно від того, чи є позитивне чи від’ємне число.)


6
Що сказати, ~~це скорочений спосіб (і, можливо, хороше рішення?) Для створення функції скорочення , але, очевидно, у JavaScript .
ruffin

4
JSLint поскаржиться на використання ~~.
Річард Кук

1
Спробуйте Math.trunc ()
Xitalogy

30

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

Ви можете використовувати його для перетворення масиву 1-го елемента в ціле число. Плаваючі точки перетворюються за правилом С, тобто. усічення дробової частини.

Потім оператор ~ ~ інвертує біти назад, так що ви знаєте, що у вас буде ціле число. Це не те саме, що примусити значення до булевого висловлення в умові, оскільки порожній об'єкт {} оцінює як істинне, тоді як ~~ {} оцінює на хибне.

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

1
Дякую за всі приклади тут, Шанті, це справді допомогло!
Шейн Томлінсон

6
також~~undefined // 0
чемпіон

1
також~~null // 0
chovy

Технічно у вас неправильний порядок. Другий ~робить те, що ви описали, перший ~робить і навпаки. ~Оператор є унарними і interpereted справа наліво ~~X, як ~(~X)не подобається (~~)X(що було б синтаксичну помилку)
yunzen

20

У 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);
}

6
Дещо дивно, ~~ швидше, ніж Math.trunc, jsperf.com/math-trunc-vs-double-bitwise-not-operator . Хоча, не все стосується швидкості; читабельність теж.
Гаджус

3
Існує важлива різниця між ~~ і Math.trunc: якщо ви передаєте рядок або NaN або будь-яку іншу річ, яка не є числом, Math.trunc поверне NaN, а ~~ завжди поверне число, у тих випадках це повернеться 0.
Бузінас

За інформацією jsperf.com/math-trunc-vs-double-bitwise-not-operator, Math.trunc незначно швидше, ніж ~~ у Chrome 59+ .
Jack Steam

12

~, Здається, робить -(N+1). Отже, ~2 == -(2 + 1) == -3якщо ви зробите це знову на -3, це повертає його назад: ~-3 == -(-3 + 1) == 2це, ймовірно, просто перетворює рядок у число у зворотній спосіб.

Дивіться цю тему: http://www.sitepoint.com/forums/showthread.php?t=663275

Також більш детальна інформація доступна тут: http : //dreaminginja JavaScript.wordpress.com/2008/07/04/28/


Дякуємо за посилання Drackir!
Шейн Томлінсон


4

Просто трохи попередження. Інші відповіді тут зіткнулися з деякими проблемами.

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

По-перше, ~~ не працює у дуже великій кількості.

~~1000000000000 == -727279968

В якості альтернативи можна використовувати Math.trunc()(як згадував Gajus, 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

Перетворення рядків у числа

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

Тільда ​​(~) має алгоріму - (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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.