Javascript'te Number.sign ()


101

Numaranın işaretini ( işaret fonksiyonu ) bulmanın önemsiz bir yolu olup olmadığını merak ediyor musunuz?
Bariz olandan daha kısa / daha hızlı / daha zarif çözümler olabilir

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

Kısa cevap!

Bunu kullanın ve güvenli ve hızlı olun (kaynak: moz )

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

Sen performans ve tip-Coercing karşılaştırma bakmak isteyebilirsiniz keman

Uzun zaman geçti. Dahası, esas olarak tarihsel nedenlerle.


Sonuçlar

Şimdilik şu çözümlere sahibiz:


1. Açık ve hızlı

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

1.1. Kbec'den değişiklik - bir tür daha az, daha yüksek performanslı, daha kısa [en hızlı]

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

Dikkat: sign("0") -> 1


2. Zarif, kısa, o kadar hızlı değil [en yavaş]

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

uyarıyoruz: sign(+-Infinity) -> NaN ,sign("0") -> NaN

İtibariyle Infinitybu çözüm tam doğru görünmüyor JS yasal sayıdır.


3. Sanat ... ama çok yavaş [en yavaş]

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

4. Bit-shift hızlı kullanmak
, ancaksign(-Infinity) -> 0

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

5. Tür açısından güvenli [megafast]

! Görünüşe göre tarayıcılar (özellikle Chrome'un v8'i) bazı sihirli optimizasyonlar yapıyor ve bu çözüm, 2 ekstra işlem içermesine ve mantıksal olarak hiçbir zaman daha hızlı olamamasına rağmen (1.1) 'den bile çok daha iyi performans gösteriyor.

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

Araçlar

  • jsperf performans testleri;
  • keman - tip atma testleri;

İyileştirmelere açığız!


[Offtopic] Kabul edilen cevap

  • Andrey Tarantsov - Sanat için +100, ama ne yazık ki bariz yaklaşımdan yaklaşık 5 kat daha yavaş

  • Frédéric Hamidi - bir şekilde en çok oylanan cevap (yazarken) ve biraz havalı, ama kesinlikle işlerin nasıl yapılması gerektiği değil, imho. Ayrıca, aynı zamanda sayı olan Sonsuzluk sayılarını da doğru şekilde işlemez.

  • kbec - bariz çözümün bir geliştirmesidir. O kadar devrimci değil, ama hep birlikte ele aldığımda bu yaklaşımı en iyisi olarak görüyorum. Ona oy verin :)


3
mesele şu ki, bazen 0özel bir durumdur
2011'de

1
Her algoritmayı test etmek için bir dizi JSPerf testi (farklı türde girdilerle) yaptım, burada bulabilirsin: jsperf.com/signs Sonuçlar bu yazıda listelendiği gibi olmayabilir!
Alba Mendez

2
@ memnun, hangisi? Elbette, test everythingsürümü çalıştırırsanız , Safe özel değerleri test etmeyi reddedecektir, bu nedenle daha hızlı olacaktır! only integersBunun yerine testi çalıştırmayı deneyin . Ayrıca, JSPerf sadece işini yapıyor, bu bir sevme meselesi değil. :)
Alba Mendez

2
Jsperf testlerine göre typeof x === "number", performansa biraz sihir kattığı ortaya çıktı . Lütfen, açıklığa kavuşturmak için özellikle FF, Opera ve IE olmak üzere daha fazla çalışma yapın.
2013,

4
Tamlık için Math.sign(), FF25'te görünen ve Chrome'da yakında çıkacak olan (0 === 0, "Güvenli" kadar hızlı değil) için yeni bir jsperf.com/signs/7 testi ekledim .
Alex K.

Yanıtlar:



28

Sayının mutlak değerine bölünmesi de işaretini verir. Kısa devre yapan mantıksal AND işlecini kullanmak bize özel duruma izin verir, 0böylece onunla bölmek zorunda kalmayız:

var sign = number && number / Math.abs(number);

6
Muhtemelen isterdi var sign = number && number / Math.abs(number);durumdanumber = 0
NullUserException

@NullUserException, kesinlikle haklısınız, 0özel kasalı olması gerekiyor. Cevap buna göre güncellendi. Teşekkürler :)
Frédéric Hamidi

Şimdilik en iyisisin. Ama umarım gelecekte daha fazla cevap olur.
disfated

24

Aradığınız işleve signum adı verilir ve bunu uygulamanın en iyi yolu şudur:

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

3
Bekle. Hata var: for (x = -2; x <= 2; x ++) console.log ((x> 1) - (x <1)); (x = -2; x <= 2; x ++) console.log ((x> 0) - (x <0)) için [-1, -1, -1, 0, 1] verir;
doğruyu

13

Bu, JavaScript'in (ECMAScript'in) işaretli sıfırlarını desteklememeli mi? "Megafast" işlevinde 0 yerine x döndürüldüğünde çalışıyor gibi görünüyor:

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

Bu, onu ECMAScript'in Math.sign ( MDN ) taslağıyla uyumlu hale getirir :

X'in pozitif mi, negatif mi yoksa sıfır mı olduğunu gösteren x'in işaretini döndürür.

  • X NaN ise sonuç NaN olur.
  • X -0 ise sonuç -0 olur.
  • X +0 ise sonuç +0 olur.
  • X negatifse ve −0 değilse, sonuç -1'dir.
  • X pozitifse ve +0 değilse, sonuç +1 olur.

İnanılmaz derecede hızlı ve ilginç mekanizma, etkilendim. Daha fazla test bekleniyor.
kbec

10

En son tarayıcılarda neler olup bittiğiyle ilgilenen kişiler için, ES6 sürümünde yerel bir Math.sign yöntemi vardır. Desteği buradan kontrol edebilirsiniz .

Temel olarak döndüren -1, 1, 0veyaNaN

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

4
var sign = number >> 31 | -number >>> 31;

Infinity'ye ihtiyacınız yoksa ve sayının openjdk-7 kaynağında bulunan bir tam sayı olduğunu biliyorsanız süper hızlı: java.lang.Integer.signum()


1
Bu, -0,5 gibi küçük negatif kesirler için başarısız olur. (Görünüşe göre kaynak, özellikle Tamsayılar için bir uygulamadan geliyor)
15'te

1

Bunu sadece eğlence için ekleyeceğimi düşündüm:

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

0 ve NaN -1 döndürecektir
, +/- Infinity


1

Tüm numaraları çalışmaları yanı sıra bu bir çözüm 0ve -0yanı sıra Infinityve -Infinityşöyledir:

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

Daha fazla bilgi için " +0 ve -0 aynı mı? " Sorusuna bakın .


Uyarı: artık standart dahil olmak üzere bu cevaplar, hiçbiri Math.signdavasında irade çalışması 0vs -0. Bu sizin için bir sorun olmayabilir, ancak bazı fizik uygulamalarında önemli olabilir.


0

Numarayı biraz kaydırabilir ve En Önemli Bit'i (MSB) kontrol edebilirsiniz. MSB 1 ise, sayı negatiftir. 0 ise, sayı pozitiftir (veya 0).


@ NullUserException Hâlâ yanılıyor olabilirim ama "Tüm bitsel işleçlerin işlenenleri, büyük endian sırasına ve ikinin tamamlayıcı biçiminde imzalı 32 bitlik tam sayılara dönüştürülür." MDN
Brombomb

Bu hala çok fazla iş gibi görünüyor; Hala 1 ve 0'ı -1 ve 1'e dönüştürmeniz gerekiyor ve 0'a da dikkat etmeniz gerekiyor. OP sadece bunu isterse, kullanımı daha kolay olurduvar sign = number < 0 : 1 : 0
NullUserException

+1. Kaymaya gerek yok, sadece n & 0x80000000bit maskesi gibi yapabilirsiniz . n && (n & 0x80000000 ? -1 : 1)
0,1'e

@davin Tüm numaraların bu bit maskesi ile çalışması garantili mi? Fişi taktım -5e32ve bozuldu.
NullUserException

@NullUserException ఠ_ఠ, standartlar uygulandığında aynı işareti taşıyan sayılar ToInt32. Burada okursanız (bölüm 9.5), 32 bitlik bir tamsayının aralığı js Sayı türünün aralığından daha az olduğu için sayıların değerini etkileyen bir modül vardır. Yani bu değerler veya sonsuzluklar için işe yaramayacak. Yine de cevabı beğendim.
davin

0

Tam da aynı soruyu sormak üzereydim, ancak yazmayı bitirmeden bir çözüme ulaştım, bu Sorunun zaten var olduğunu gördüm, ancak bu çözümü görmedim.

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

bir üçlü ekleyerek daha hızlı görünüyor (n >> 31) + (n>0?1:0)


Çok hoş. Kodunuz (1) 'den biraz daha hızlı görünüyor. (n> 0 - 1: 0) hiçbir tür atamadan daha hızlıdır. Tek hayal kırıklığı anı işarettir (-Infinity) 0 verir. Testler güncellendi.
2013

0

Martijn'in cevabına çok benzer:

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

Daha okunaklı buluyorum. Ayrıca (veya bakış açınıza bağlı olarak), bir sayı olarak yorumlanabilecek şeyleri de topluyor; örneğin, -1ile sunulduğunda geri döner '-5'.


0

-0 ve 0'ı döndürmenin pratik anlamı görmüyorum, Math.signbu yüzden benim sürümüm :

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

Bu bir işaret işlevi değil
2016

0

Bildiğim yöntemler aşağıdaki gibidir:

Math.sign (n)

var s = Math.sign(n)

Bu yerel işlevdir, ancak bir işlev çağrısının ek yükü nedeniyle en yavaş olanıdır. Bununla birlikte, aşağıdaki diğerlerinin sadece 0 varsayabildiği 'NaN' ile ilgilenir (yani Math.sign ('abc') NaN'dir).

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

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

Bu durumda, işarete bağlı olarak yalnızca sol veya sağ taraf 1 olabilir. Bu, 1-0(1), 0-1(-1) veya 0-0(0) ile sonuçlanır .

Bunun hızı, Chrome'da bir sonraki ile boyun ve boyun görünüyor.

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

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

"İşaret-ilerleyen sağa kaydırma" kullanır. Temelde 31 kaydırma, işaret hariç tüm bitleri düşürür. İşaret ayarlanmışsa, bu -1 ile sonuçlanır, aksi takdirde 0 olur. Hakkı |, değeri boolean'a dönüştürerek pozitif testi yapar (0 veya 1 [BTW: sayısal olmayan dizeler, !!'abc'bu durumda 0 olur ve bu durumda 0 olur ve not NaN]) daha sonra bitleri birleştirmek için bitsel OR işlemi kullanır.

Bu , tarayıcılar arasında en iyi ortalama performans gibi görünüyor (en azından Chrome ve Firefox'ta en iyisi), ancak TÜMÜNDE en hızlı değil. Bazı nedenlerden dolayı, üçlü operatör IE'de daha hızlıdır.

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

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

Bazı nedenlerden dolayı IE'de en hızlı.

jsPerf

Gerçekleştirilen testler: https://jsperf.com/get-sign-from-value


0

Math.sign ile aynı sonuçları döndüren bir fonksiyona sahip iki sentim, yani işaret (-0) -> -0, işaret (-Infinity) -> -Infinity, işaret (null) -> 0 , işaret (tanımsız) -> NaN, vb.

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

Jsperf bir test veya revizyon oluşturmama izin vermiyor, size testleri sağlayamadığım için üzgünüm (jsbench.github.io'ya bir deneme yaptım, ancak sonuçlar Jsperf ile olduğundan çok daha yakın görünüyor ...)

Birisi bunu bir Jsperf revizyonuna ekleyebilirse, daha önce verilen tüm çözümlerle nasıl karşılaştırıldığını merak ederdim ...

Teşekkür ederim!

Jim.

DÜZENLE :

Yazmalıydım:

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

( (+x && -1)yerine (x && -1)) sign('abc')düzgün işlemek için (-> NaN)


0

Math.sign, IE 11'de desteklenmiyor. En iyi yanıtı Math.sign cevabı ile birleştiriyorum:

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

Artık Math.sign'ı doğrudan kullanabilirsiniz.


1
Sorumu güncellemeye ittin beni. Sorulmasının üzerinden 8 yıl geçti. Ayrıca jsfiddle'ımı es6 ve window.performance api olarak güncelledi. Ancak Math.sign'ın tip zorlamasıyla eşleştiği için mozilla'nın bir polyfill versiyonunu tercih ediyorum. Günümüzde performans pek endişe verici değil.
2019
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.