Number.sign () dalam javascript

101

Bertanya-tanya apakah ada cara nontrivial untuk menemukan tanda bilangan ( fungsi signum )?
Mungkin solusi yang lebih pendek / lebih cepat / lebih elegan daripada yang sudah jelas

var sign = number > 0 ? 1 : number < 0 ? -1 : 0;

Jawaban singkat!

Gunakan ini dan Anda akan aman dan cepat (sumber: moz )

if (!Math.sign) Math.sign = function(x) { return ((x > 0) - (x < 0)) || +x; };

Anda mungkin ingin melihat kinerja dan biola perbandingan pemaksaan tipe

Waktu sudah lama berlalu. Lebih lanjut terutama karena alasan sejarah.


Hasil

Untuk saat ini kami memiliki solusi ini:


1. Jelas dan cepat

function sign(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }

1.1. Modifikasi dari kbec - satu jenis lebih sedikit, lebih berkinerja, lebih pendek [tercepat]

function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }

peringatan: sign("0") -> 1


2. Elegan, pendek, tidak terlalu cepat [paling lambat]

function sign(x) { return x && x / Math.abs(x); }

hati-hati: sign(+-Infinity) -> NaN ,sign("0") -> NaN

Pada Infinitynomor resmi di JS, solusi ini tampaknya tidak sepenuhnya benar.


3. Seni ... tapi sangat lambat [paling lambat]

function sign(x) { return (x > 0) - (x < 0); }

4. Menggunakan bit-shift
cepat, tapisign(-Infinity) -> 0

function sign(x) { return (x >> 31) + (x > 0 ? 1 : 0); }

5. Aman untuk mengetik [megafast]

! Sepertinya browser (terutama chrome v8) membuat beberapa optimasi ajaib dan solusi ini ternyata jauh lebih berkinerja daripada yang lain, bahkan daripada (1.1) meskipun berisi 2 operasi tambahan dan secara logis tidak pernah bisa lebih cepat.

function sign(x) {
    return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN;
}

Alat

Perbaikan dipersilakan!


[Offtopic] Jawaban yang diterima

  • Andrey Tarantsov - +100 untuk seni, tapi sayangnya itu sekitar 5 kali lebih lambat dari pendekatan yang sudah jelas

  • Frédéric Hamidi - entah bagaimana jawaban yang paling disukai (untuk saat ini menulis) dan itu agak keren, tapi jelas bukan bagaimana hal-hal harus dilakukan, imho. Juga tidak menangani nomor Infinity dengan benar, yang juga merupakan angka, lho.

  • kbec - adalah perbaikan dari solusi yang sudah jelas. Tidak terlalu revolusioner, tetapi secara keseluruhan saya menganggap pendekatan ini sebagai yang terbaik. Pilih dia :)

disfated
sumber
3
intinya adalah bahwa kadang-kadang 0adalah kasus khusus
dicabut
1
Saya telah membuat serangkaian tes JSPerf (dengan jenis input yang berbeda) untuk menguji setiap algoritma, yang dapat ditemukan di sini: jsperf.com/signs Hasilnya mungkin tidak seperti yang tercantum dalam posting ini!
Alba Mendez
2
@ puas, yang mana? Tentu saja, jika Anda menjalankan test everythingversi tersebut, Safe akan menolak untuk menguji nilai-nilai khusus, jadi akan lebih cepat! Coba jalankan only integerspengujian sebagai gantinya. Selain itu, JSPerf hanya melakukan tugasnya, bukan tentang menyukainya. :)
Alba Mendez
2
Menurut tes jsperf, ternyata itu typeof x === "number"memberikan keajaiban pada kinerja. Tolong, buat lebih banyak berjalan, terutama FF, Opera dan IE untuk membuatnya jelas.
disfate
4
Untuk kelengkapan saya menambahkan tes baru jsperf.com/signs/7 untuk Math.sign()(0 === 0, tidak secepat "Aman") yang muncul di FF25 dan akan datang di chrome.
Alex K.

Jawaban:

79

Versi solusi cepat yang lebih elegan:

var sign = number?number<0?-1:1:0
kbec
sumber
4
-1 untuk menumbuk terner Anda bersamavar sign = (number)? ((number < 0)? -1 : 1 ) : 0
Patrick Michaelsen
3
Math.sign(number)
Илья Зеленько
28

Membagi bilangan dengan nilai absolutnya juga memberikan tandanya. Menggunakan operator logika AND yang hubung singkat memungkinkan kita untuk kasus khusus 0sehingga kita tidak berakhir membaginya:

var sign = number && number / Math.abs(number);
Frédéric Hamidi
sumber
6
Anda mungkin ingin var sign = number && number / Math.abs(number);berjaganumber = 0
NullUserException
@NullUserException, Anda benar sekali, 0perlu menggunakan kasing khusus. Jawaban diperbarui sesuai. Terima kasih :)
Frédéric Hamidi
Kamu yang terbaik untuk saat ini. Tapi saya berharap akan ada lebih banyak jawaban di masa depan.
disfated
24

Fungsi yang Anda cari disebut signum , dan cara terbaik untuk mengimplementasikannya adalah:

function sgn(x) {
  return (x > 0) - (x < 0);
}
Andrey Tarantsov
sumber
3
Tunggu. Ada kesalahan: for (x = -2; x <= 2; x ++) console.log ((x> 1) - (x <1)); memberikan [-1, -1, -1, 0, 1] untuk (x = -2; x <= 2; x ++) console.log ((x> 0) - (x <0)); memberikan yang benar [-1, -1, 0, 1, 1]
disfate
13

Haruskah ini tidak mendukung nol yang bertanda tangan JavaScript (ECMAScript)? Tampaknya berfungsi saat mengembalikan x daripada 0 dalam fungsi "megafast":

function sign(x) {
    return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? x : NaN : NaN;
}

Ini membuatnya kompatibel dengan draf ECMAScript's Math.sign ( MDN ):

Mengembalikan tanda x, yang menunjukkan apakah x positif, negatif, atau nol.

  • Jika x adalah NaN, hasilnya adalah NaN.
  • Jika x adalah −0, hasilnya −0.
  • Jika x adalah +0, hasilnya adalah +0.
  • Jika x negatif dan bukan −0, hasilnya −1.
  • Jika x positif dan bukan +0, hasilnya +1.
Martijn
sumber
Mekanismenya sangat cepat dan menarik, saya terkesan. Menunggu tes lainnya.
kbec
10

Bagi orang yang tertarik dengan apa yang sedang terjadi dengan browser terbaru, di versi ES6 ada metode Math.sign asli . Anda dapat memeriksa dukungannya di sini .

Pada dasarnya itu kembali -1, 1, 0atauNaN

Math.sign(3);     //  1
Math.sign(-3);    // -1
Math.sign('-3');  // -1
Math.sign(0);     //  0
Math.sign(-0);    // -0
Math.sign(NaN);   // NaN
Math.sign('foo'); // NaN
Math.sign();      // NaN
Salvador Dali
sumber
4
var sign = number >> 31 | -number >>> 31;

Superfast jika Anda tidak membutuhkan Infinity dan mengetahui bahwa nomor tersebut adalah bilangan bulat, ditemukan di sumber openjdk-7: java.lang.Integer.signum()

Toxiro
sumber
1
Ini gagal untuk pecahan negatif kecil seperti -0,5. (Sepertinya sumbernya berasal dari implementasi untuk Integer secara khusus)
starwed
1

Saya pikir saya akan menambahkan ini hanya untuk bersenang-senang:

function sgn(x){
  return 2*(x>0)-1;
}

0 dan NaN akan mengembalikan -1
berfungsi dengan baik pada +/- Infinity

jaya
sumber
1

Solusi yang berfungsi pada semua angka, serta 0dan -0, serta Infinitydan -Infinity, adalah:

function sign( number ) {
    return 1 / number > 0 ? 1 : -1;
}

Lihat pertanyaan " Apakah +0 dan -0 sama? " Untuk informasi lebih lanjut.


Peringatan: Tidak ada jawaban, termasuk sekarang standar Math.signakan bekerja pada kasus 0vs -0. Ini mungkin tidak menjadi masalah bagi Anda, tetapi dalam implementasi fisika tertentu itu mungkin penting.

Andy Ray
sumber
0

Anda dapat menggeser nomor tersebut dan memeriksa Bit Paling Signifikan (MSB). Jika MSB adalah 1 maka angkanya negatif. Jika 0 maka angkanya positif (atau 0).

Brombomb
sumber
@ NullUserException Saya masih bisa salah tetapi dari pembacaan saya "Operand dari semua operator bitwise diubah menjadi integer 32-bit bertanda tangan dalam urutan big-endian dan dalam format komplemen dua." diambil dari MDN
Brombomb
Itu masih tampak seperti banyak pekerjaan; Anda masih harus mengubah 1 dan 0 menjadi -1 dan 1, dan 0 juga harus dijaga. Jika OP hanya menginginkan itu, akan lebih mudah untuk menggunakannyavar sign = number < 0 : 1 : 0
NullUserException
+1. Tidak perlu bergeser, Anda bisa melakukan n & 0x80000000seperti bitmask. Adapun konversi ke 0,1, -1:n && (n & 0x80000000 ? -1 : 1)
davin
@davin Apakah semua angka dijamin bekerja dengan bitmask itu? Saya terpasang -5e32dan itu rusak.
NullUserException
@NullUserException ఠ_ఠ, angka yang memiliki tanda yang sama saat menerapkan standar ToInt32. Jika Anda membaca di sana (bagian 9.5) ada modulus yang mempengaruhi nilai angka karena kisaran integer 32-bit lebih kecil dari kisaran tipe Nomor js. Jadi itu tidak akan berhasil untuk nilai-nilai itu, atau ketidakterbatasan. Saya masih suka jawabannya.
davin
0

Saya baru saja akan menanyakan pertanyaan yang sama, tetapi menemukan solusi sebelum saya selesai menulis, melihat Pertanyaan ini sudah ada, tetapi tidak melihat solusi ini.

(n >> 31) + (n > 0)

tampaknya akan lebih cepat dengan menambahkan terner sekalipun (n >> 31) + (n>0?1:0)

Moritz Roessler
sumber
Sangat bagus. Kode Anda tampaknya sedikit lebih cepat daripada (1). (n> 0? 1: 0) lebih cepat karena tidak ada tipe cast. Satu-satunya momen mengecewakan adalah tanda (-Infinity) memberikan 0. Tes yang diperbarui.
disfated
0

Sangat mirip dengan jawaban Martijn ini

function sgn(x) {
    isNaN(x) ? NaN : (x === 0 ? x : (x < 0 ? -1 : 1));
}

Saya merasa lebih mudah dibaca. Juga (atau, tergantung pada sudut pandang Anda, bagaimanapun), itu juga menyimpan hal-hal yang dapat diartikan sebagai angka; misalnya, kembali -1saat disajikan dengan '-5'.

equaeghe
sumber
0

Saya tidak melihat arti praktis mengembalikan -0 dan 0 dari Math.signjadi versi saya adalah:

function sign(x) {
    x = Number(x);
    if (isNaN(x)) {
        return NaN;
    }
    if (x === -Infinity || 1 / x < 0) {
        return -1;
    }
    return 1;
};

sign(100);   //  1
sign(-100);  // -1
sign(0);     //  1
sign(-0);    // -1
Alexander Shutau
sumber
Ini bukan fungsi signum
disfated
0

Metode yang saya ketahui adalah sebagai berikut:

Tanda matematika (n)

var s = Math.sign(n)

Ini adalah fungsi asli, tetapi yang paling lambat dari semuanya karena overhead panggilan fungsi. Namun ia menangani 'NaN' di mana yang lain di bawah ini mungkin hanya menganggap 0 (yaitu, tanda Matematika ('abc') adalah NaN).

((n> 0) - (n <0))

var s = ((n>0) - (n<0));

Dalam hal ini hanya sisi kiri atau kanan yang dapat menjadi 1 berdasarkan tanda. Ini akan menghasilkan 1-0(1), 0-1(-1), atau 0-0(0).

Kecepatan yang satu ini tampaknya leher dan leher dengan yang berikutnya di bawah di Chrome.

(n >> 31) | (!! n)

var s = (n>>31)|(!!n);

Menggunakan "Pergeseran kanan yang menyebarkan tanda". Pada dasarnya menggeser 31 tetes semua bit kecuali tanda. Jika tanda diset, ini menghasilkan -1, jika tidak maka 0. Benar |tes positif dengan mengubah nilai menjadi boolean (0 atau 1 [BTW: string non-numerik, seperti !!'abc', menjadi 0 dalam kasus ini, dan bukan NaN]) lalu menggunakan operasi bitwise OR untuk menggabungkan bit.

Ini tampaknya kinerja rata-rata terbaik di seluruh browser (paling tidak terbaik di Chrome dan Firefox), tetapi bukan yang tercepat di SEMUA browser tersebut. Untuk beberapa alasan, operator terner lebih cepat di IE.

n? n <0? -1: 1: 0

var s = n?n<0?-1:1:0;

Tercepat di IE karena alasan tertentu.

jsPerf

Pengujian dilakukan: https://jsperf.com/get-sign-from-value

James Wilkins
sumber
0

Dua sen saya, dengan fungsi yang mengembalikan hasil yang sama seperti yang akan dilakukan Math.sign, yaitu tanda (-0) -> -0, tanda (-Infinity) -> -Infinity, tanda (null) -> 0 , tanda (tidak ditentukan) -> NaN, dll.

function sign(x) {
    return +(x > -x) || (x && -1) || +x;
}

Jsperf tidak mengizinkan saya membuat pengujian atau revisi, maaf karena tidak dapat memberi Anda pengujian (saya telah mencoba jsbench.github.io, tetapi hasilnya tampak lebih mirip satu sama lain daripada dengan Jsperf ...)

Jika seseorang dapat menambahkannya ke revisi Jsperf, saya akan penasaran untuk melihat bagaimana perbandingannya dengan semua solusi yang diberikan sebelumnya ...

Terima kasih!

Jim.

EDIT :

Saya seharusnya menulis:

function sign(x) {
    return +(x > -x) || (+x && -1) || +x;
}

( (+x && -1)bukan (x && -1)) untuk menangani sign('abc')dengan benar (-> NaN)

Jimshell
sumber
0

Math.sign tidak didukung di IE 11. Saya menggabungkan jawaban terbaik dengan jawaban Math.sign:

Math.sign = Math.sign || function(number){
    var sign = number ? ( (number <0) ? -1 : 1) : 0;
    return sign;
};

Sekarang, seseorang dapat menggunakan Math.sign secara langsung.

sudip
sumber
1
Anda mendorong saya untuk memperbarui pertanyaan saya. 8 tahun berlalu sejak diminta. Juga memperbarui jsfiddle saya ke es6 dan window.performance api. Tapi saya lebih suka versi mozilla sebagai polyfill karena cocok dengan pemaksaan tipe Math.sign. Kinerja tidak terlalu menjadi perhatian saat ini.
disfated