O que ~~ ("double til") faz em Javascript?

206

Hoje eu estava checando uma biblioteca de física de jogos online e me deparei com o operador ~~. Eu sei que um único ~ é um NÃO bit a bit, isso tornaria ~~ um NÃO de um NÃO, o que daria o mesmo valor, não é?

Shane Tomlinson
fonte

Respostas:

248

Ele remove tudo após o ponto decimal porque os operadores bit a bit implicitamente convertem seus operandos em números inteiros de 32 bits assinados. Isso funciona se os operandos são números ou cadeias (ponto flutuante) e o resultado é um número.

Em outras palavras, produz:

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

somente se x estiver entre - (2 31 ) e 2 31 - 1. Caso contrário, ocorrerá um estouro e o número será "contornado".

Isso pode ser considerado útil para converter o argumento de seqüência de caracteres de uma função em um número, mas, devido à possibilidade de estouro e por estar incorreto para uso com números não inteiros, eu não o usaria dessa maneira, exceto para "code golf" ( ou seja, cortar inutilmente os bytes do código-fonte do seu programa em detrimento da legibilidade e robustez). Eu usaria +xou Number(x)não.


Como esse é o NÃO do NÃO

O número -43,2, por exemplo, é:

-43,2 10 = 11111111111111111111111111010101 2

como um número binário de 32 bits assinado (complemento de dois). (JavaScript ignora o que está depois do ponto decimal.) A inversão dos bits fornece:

NÃO -43 10 = 000000000000000000000000000000101010 2 = 42 10

A inversão novamente fornece:

NÃO 42 10 = 11111111111111111111111111010101 2 = -43 10

Isso difere de Math.floor(-43.2)que os números negativos são arredondados para zero, não para longe dele. (A função de piso, que seria igual a -44, sempre arredonda para o próximo número inteiro inferior, independentemente de o número ser positivo ou negativo.)

PleaseStand
fonte
6
Ou seja, ~~é uma maneira abreviada (e possivelmente uma boa solução?) Para criar uma função truncada , mas obviamente em javascript .
Ruffin
4
JSLint irá reclamar sobre o uso de ~~.
Richard Cook
1
Tente Math.trunc ()
Xitalogy
30

O primeiro operador ~ força o operando a um número inteiro (possivelmente após coagir o valor a uma string ou a um valor booleano) e depois inverte os 31 bits mais baixos. Oficialmente, os números do ECMAScript são todos de ponto flutuante, mas alguns são implementados como números inteiros de 31 bits no mecanismo SpiderMonkey.

Você pode usá-lo para transformar uma matriz de 1 elemento em um número inteiro. Os pontos flutuantes são convertidos de acordo com a regra C, ie. truncamento da parte fracionária.

O segundo operador ~ inverte os bits de volta, para que você saiba que terá um número inteiro. Isso não é o mesmo que coagir um valor para booleano em uma declaração de condição, porque um objeto vazio {} é avaliado como verdadeiro, enquanto ~~ {} é avaliado como falso.

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
Shanti
fonte
1
Obrigado por todos os exemplos aqui Shanti, realmente ajudou!
Shane Tomlinson
6
também~~undefined // 0
rampion
1
Também~~null // 0
chovy 03/08
Tecnicamente, você tem a ordem errada. O segundo ~faz o que você descreveu o primeiro ~e vice-versa. O ~operador é um operador unário e é interoperado da direita para a esquerda ~~Xe ~(~X)não como (~~)X(o que seria um erro de sintaxe)
yunzen 15/06
20

No ECMAScript 6, o equivalente a ~~é Math.trunc :

Retorna a parte integral de um número removendo quaisquer dígitos fracionários. Não arredonda nenhum número.

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

O polyfill:

function trunc(x) {
    return x < 0 ? Math.ceil(x) : Math.floor(x);
}
Gajus
fonte
6
Surpreendentemente, o ~~ é mais rápido que o Math.trunc, jsperf.com/math-trunc-vs-double-bitwise-not-operator . No entanto, nem tudo é sobre velocidade; legibilidade também.
Gajus
3
Existe uma diferença importante entre ~~ e Math.trunc: se você passar uma string, ou NaN ou qualquer coisa que não seja um número, Math.trunc retornará NaN e ~~ sempre retornará um número; nesses casos, ele retornará 0.
Buzinas
O Math.trunc é um pouco mais rápido que o ~~ no Chrome 59+, de acordo com jsperf.com/math-trunc-vs-double-bitwise-not-operator .
Jack Steam
12

O que ~parece fazer -(N+1). Portanto, ~2 == -(2 + 1) == -3se você fizer novamente com -3, ele voltará: ~-3 == -(-3 + 1) == 2provavelmente converte uma string em um número de maneira arredondada.

Consulte este tópico: http://www.sitepoint.com/forums/showthread.php?t=663275

Além disso, informações mais detalhadas estão disponíveis aqui: http://dreaminginjavascript.wordpress.com/2008/07/04/28/

Richard Marskell - Drackir
fonte
Obrigado pelos links Drackir!
Shane Tomlinson
7

Dado ~Né -(N+1), ~~Né então -(-(N+1) + 1). O que, evidentemente, leva a um truque legal .

James Sumners
fonte
Tem que rolar para baixo ao comentário de Matt vê-lo em uso adequado;)
mplungjan
4

Apenas um aviso. As outras respostas aqui me causaram alguns problemas.

A intenção é remover qualquer coisa após o ponto decimal de um número de ponto flutuante, mas há alguns casos de canto que o tornam um risco de bug. Eu recomendo evitar ~~.

Primeiro, o ~~ não funciona em números muito grandes.

~~1000000000000 == -727279968

Como alternativa, use Math.trunc()(como Gajus mencionou, Math.trunc()retorna a parte inteira de um número de ponto flutuante, mas só está disponível no JavaScript compatível com ECMAScript 6). Você sempre pode criar seus próprios ambientes Math.trunc()não-ECMAScript-6 fazendo o seguinte:

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

Eu escrevi um post sobre isso para referência: http://bitlords.blogspot.com/2016/08/the-double-tilde-x-technique-in.html

JSideris
fonte
1

Aqui está um exemplo de como esse operador pode ser usado com eficiência, onde faz sentido usá-lo:

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

Fonte:

Consulte a seção Interagindo com pontos

cssyphus
fonte
1

Convertendo seqüências de caracteres em números

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
}

fonte

Mike
fonte
1

Tilde (~) tem um algorihm - (N + 1)

Por exemplo:

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

O til duplo é - (- (N + 1) +1)

Por exemplo:

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

O til triplo é - (- (- (N + 1) +1) +1)

Por exemplo:

~~~2 = -(-(-(2+1)+1)+1) = -3
~~~3 = -(-(-(3+1)+1)+1) = -4
CroMagnon
fonte