Apa yang dilakukan ~~ (“double tilde”) dalam Javascript?

206

Saya sedang memeriksa perpustakaan fisika game online hari ini dan menemukan operator ~~. Saya tahu satu ~ TIDAK sedikit pun, akankah itu membuat ~~ TIDAK dari TIDAK, yang akan memberikan kembali nilai yang sama, bukan?

Shane Tomlinson
sumber

Jawaban:

248

Ini menghapus semuanya setelah titik desimal karena operator bitwise secara implisit mengubah operan mereka menjadi bilangan bulat 32-bit yang ditandatangani. Ini berfungsi baik operan berupa angka atau string, dan hasilnya adalah angka.

Dengan kata lain, itu menghasilkan:

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

hanya jika x berada di antara - (2 31 ) dan 2 31 - 1. Jika tidak, overflow akan terjadi dan nomor akan "membungkus".

Ini mungkin dianggap berguna untuk mengubah argumen string fungsi menjadi angka, tetapi keduanya karena kemungkinan overflow dan bahwa itu salah untuk digunakan dengan non-integer, saya tidak akan menggunakannya seperti itu kecuali untuk "kode golf" ( yaitu tanpa memotong poin dari kode sumber program Anda dengan mengorbankan keterbacaan dan ketahanan). Saya akan menggunakan +xatau Number(x)sebagai gantinya.


Bagaimana ini BUKAN BUKAN BUKAN

Angka -43.2, misalnya adalah:

-43.2 10 = 11111111111111111111111111010101 2

sebagai nomor biner 32-bit yang ditandatangani (komplemen dua). (JavaScript mengabaikan apa yang setelah titik desimal.) Membalik bit memberi:

TIDAK -43 10 = 0000000000000000000000000010101010 2 = 42 10

Pembalikan lagi memberi:

BUKAN 42 10 = 11111111111111111111111111010101 2 = -43 10

Ini berbeda dari Math.floor(-43.2)angka negatif dibulatkan ke nol, tidak jauh dari itu. (Fungsi lantai, yang akan sama dengan -44, selalu dibulatkan ke bilangan bulat bawah berikutnya, terlepas dari apakah angka tersebut positif atau negatif.)

PleaseStand
sumber
6
Artinya, ~~adalah cara singkat (dan mungkin solusi yang baik?) Untuk membuat fungsi truncate , tetapi jelas dalam javascript .
ruffin
4
JSLint akan mengeluh tentang penggunaan ~~.
Richard Cook
1
Coba Math.trunc ()
Xitalogy
30

Operator ~ pertama memaksa operan ke integer (mungkin setelah memaksa nilai ke string atau boolean), kemudian membalikkan 31 bit terendah. Nomor ECMAScript resmi semuanya adalah floating-point, tetapi beberapa angka diimplementasikan sebagai bilangan bulat 31-bit di mesin SpiderMonkey.

Anda bisa menggunakannya untuk mengubah array 1 elemen menjadi integer. Floating-point dikonversi sesuai dengan aturan C, yaitu. pemotongan bagian fraksional.

Operator ~ kedua kemudian membalikkan bit kembali, sehingga Anda tahu bahwa Anda akan memiliki integer. Ini tidak sama dengan memaksa nilai ke boolean dalam pernyataan kondisi, karena objek kosong {} bernilai true, sedangkan ~~ {} bernilai 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
Shanti
sumber
1
Terima kasih untuk semua contoh di sini, Shanti, ini sangat membantu!
Shane Tomlinson
6
juga~~undefined // 0
rampion
1
juga~~null // 0
chovy
Secara teknis Anda salah urutan. Yang kedua ~melakukan apa yang Anda deskripsikan ~lakukan pertama dan sebaliknya. The ~operator adalah operator unary dan interpereted dari kanan ke kiri ~~Xseperti ~(~X)tidak seperti (~~)X(yang akan menjadi kesalahan sintaks)
yunzen
20

Dalam ECMAScript 6, setara dengan ~~adalah Math.trunc :

Mengembalikan bagian integral dari angka dengan menghapus digit fraksional apa pun. Itu tidak membulatkan angka apa pun.

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

Polyfill:

function trunc(x) {
    return x < 0 ? Math.ceil(x) : Math.floor(x);
}
Gajus
sumber
6
Agak mengherankan, ~~ lebih cepat dari Math.trunc, jsperf.com/math-trunc-vs-double-bitwise-not-operator . Padahal, tidak semuanya tentang kecepatan; keterbacaan juga.
Gajus
3
Ada perbedaan penting antara ~~ dan Math.trunc: jika Anda meneruskan string, atau NaN atau hal apa pun yang bukan angka, Math.trunc akan mengembalikan NaN, dan ~~ akan selalu mengembalikan angka, dalam kasus itu, itu akan kembali 0.
Buzinas
Math.trunc sedikit lebih cepat daripada ~~ di Chrome 59+, menurut jsperf.com/math-trunc-vs-double-bitwise-not-operator .
Jack Steam
12

The ~tampaknya melakukan -(N+1). Jadi, ~2 == -(2 + 1) == -3jika Anda melakukannya lagi pada -3, ia akan mengembalikannya: ~-3 == -(-3 + 1) == 2Ini mungkin hanya mengubah string menjadi angka dengan cara berputar-putar.

Lihat utas ini: http://www.sitepoint.com/forums/showthread.php?t=663275

Juga, info lebih rinci tersedia di sini: http://dreaminginjavascript.wordpress.com/2008/07/04/28/

Richard Marskell - Drackir
sumber
Terima kasih atas tautannya Drackir!
Shane Tomlinson
7

Diberikan ~Nadalah -(N+1), ~~Nlalu -(-(N+1) + 1). Yang jelas mengarah pada trik yang rapi .

James Sumners
sumber
Harus gulir ke bawah ke komentar Matt untuk melihatnya dalam penggunaan yang tepat;)
mplungjan
4

Hanya sedikit peringatan. Jawaban lain di sini membuat saya kesulitan.

Tujuannya adalah untuk menghapus apa pun setelah titik desimal dari angka titik mengambang, tetapi memiliki beberapa kasus sudut yang membuatnya menjadi bahaya bug. Saya sarankan menghindari ~~.

Pertama, ~~ tidak bekerja pada angka yang sangat besar.

~~1000000000000 == -727279968

Sebagai alternatif, gunakan Math.trunc()(seperti yang disebutkan Gajus, Math.trunc()mengembalikan bagian integer dari angka floating point tetapi hanya tersedia di JavaScript yang sesuai dengan ECMAScript 6). Anda selalu dapat membuat sendiri Math.trunc()untuk lingkungan non-ECMAScript-6 dengan melakukan ini:

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

Saya menulis posting blog tentang ini untuk referensi: http://bitlords.blogspot.com/2016/08/the-double-tilde-x-technique-in.html

JSideris
sumber
1

Berikut adalah contoh bagaimana operator ini dapat digunakan secara efisien, di mana masuk akal untuk menggunakannya:

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

Sumber:

Lihat bagian Berinteraksi dengan poin

cssyphus
sumber
1

Mengubah String ke Angka

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 adalah 0

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

sumber

Mike
sumber
1

Tilde (~) memiliki algorihm - (N + 1)

Untuk memeriksa:

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

Double tilde adalah - (- (N + 1) +1)

Sebagai contoh:

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

Triple tilde adalah - (- (- (N + 1) +1) +1)

Sebagai contoh:

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