JavaScript'te kalanla tamsayı bölümü?


Yanıtlar:


1239

Bir sayı yve bazı bölen xiçin ( quotient) ve kalan ( remainder) bölümünü şu şekilde hesaplayın :

var quotient = Math.floor(y/x);
var remainder = y % x;

84
%, JavaScript'te yüzer öğeler üzerinde çalışır (bu, diğer birçok dilden farklıdır), bu da istenmez: 3.5 % 21.5 olarak değerlendirilir. Gerektiğinde

16
Matematikte -4.5'in ayrılmaz parçası -5'tir, çünkü -5 "hala -4.5'ten düşük olan mümkün olan en yüksek integral sayısıdır".
14'te sert

9
Bununla birlikte, negatif sayılar hakkında ne yapmaya karar verirseniz verin, bölüm ve geri kalanı boyunca tutarlı olmalıdır. Birlikte floorve %birlikte kullanmak bu şekilde tutarlı değildir. Ya (negatif geri kalanlara izin vermek) truncyerine kullanın floorya da geri kalanını almak için çıkarma kullanın ( rem = y - div * x).
Mark Reed

7
Eğer kalan hesaplamak için gidiyoruz 1. remneyse, bölüm elde edebilirsiniz divdöşeme olmadan daha hızlı: (y - rem) / x. 2. Bu arada, Donald Knuth'un önerilen tanımı (işaret-maç-bölen, geri kalan yani Öklid modülü veya JavaScript işaret-maç-bölme) tarafından modulo işlemi JavaScript olarak kodlayabileceğimiz şeydir function mod (a, n) { return a % n + (Math.sign(a) !== Math.sign(n) ? n : 0); }.
Aaron Mansheim

1
-9 / 2 = -4.5. Daha sonra -5 olan -4.5'in zeminini alırsınız. -5'in -4.5'ten az olduğunu ve kat işleminin belirli bir değerden daha küçük en büyük tamsayı olarak tanımlandığını unutmayın.
Mark Reed

371

Bitsel operatörler konusunda uzman değilim, ancak tüm sayıyı almanın başka bir yolu:

var num = ~~(a / b);

Bu, negatif sayılar için de düzgün çalışacak Math.floor(), yanlış yönde yuvarlanacaktır.

Bu da doğru görünüyor:

var num = (a / b) >> 0;

84
Son 20 dakikasını anlamaya çalışırken harcadığım bir a/b | 0
diğeri

18
@ user113716 @BlueRaja Bitwise işlemleri yalnızca tamsayı türlerinde mantıklıdır ve JS (elbette) bunu bilir. ~~int, int | 0Ve int >> 0ilk bağımsız değişken değiştirmez, ancak yorumlayıcı operatöre parçasını geçmesi olun.
Aleksei Zabrodskii

15
flooradı göz önüne alındığında neredeyse yanlış yöne yuvarlanmıyor - insanların genellikle istediği yönü değil!
Mark K Cowan

19
Bu bir buu buu. a = 12447132275286670000; b = 128 Math.floor(a/b)-> 97243220900677100ve ~~(a/b)-> -1231452688.
Mirek Rusin

7
Önceliğe dikkat edin. ~~(5/2) --> 2olduğu gibi (5/2)>>0 --> 2, ama ~~(5/2) + 1 --> 3süre ~~(5/2)>>0 + 1 --> 1. ~~iyi bir seçimdir çünkü öncelik daha uygundur.
timkay

217

Firefox'ta bazı hız testleri yaptım.

-100/3             // -33.33..., 0.3663 millisec
Math.floor(-100/3) // -34,       0.5016 millisec
~~(-100/3)         // -33,       0.3619 millisec
(-100/3>>0)        // -33,       0.3632 millisec
(-100/3|0)         // -33,       0.3856 millisec
(-100-(-100%3))/3  // -33,       0.3591 millisec

/* a=-100, b=3 */
a/b                // -33.33..., 0.4863 millisec
Math.floor(a/b)    // -34,       0.6019 millisec
~~(a/b)            // -33,       0.5148 millisec
(a/b>>0)           // -33,       0.5048 millisec
(a/b|0)            // -33,       0.5078 millisec
(a-(a%b))/b        // -33,       0.6649 millisec

Yukarıda belirtilenler her biri için 10 milyon denemeye dayanmaktadır.

Sonuç: Kullanım (a/b>>0)(veya (~~(a/b))veya (a/b|0)) verimliliğinde 20 yaklaşık% kazancı elde etmek. Ayrıca, bunların Math.floorne zaman tutarsız olduklarını da unutmayın a/b<0 && a%b!=0.


54
Tamsayı bölünmesini hız için optimize etmenin yalnızca çok şey yapıyorsanız mantıklı olacağını unutmayın . Başka bir durumda en basit olanı seçmenizi tavsiye ederim (hangisi size ve iş arkadaşlarınıza en basit görünüyorsa).
mik01aj

7
@ m01 tamamen katılıyorum - böyle çevrimiçi şeylere çok fazla odaklanma var
JonnyRaa

2
@ m01 Ama hangisi daha zor: Math.floordiğer API işlevlerini ve kimin bildiklerini öğrenmek ya da ~(bitsel-değil) operatörü ve JS'de bitsel işlemlerin nasıl çalıştığını öğrenmek ve daha sonra çift ​​tilde etkisini anlamak ?
Stijn de Witt

11
İş arkadaşlarınız montajcıdaki yongaları programlamıyorsa, muhtemelen Math.floordaha iyi anlayacaklardır . Ve olmasa bile, bu googleable.
mik01aj

2
@MarkGreen evet ama sadece bunu yapmak istemeniz, garip bir biçimde yazmanız gerektiği anlamına gelmez, çünkü en hızlı, iyi bir sebep olmadan olur - genel olarak kod netliği normalde ilk endişeniz olmalıdır. Ayrıca bu testler dil değiştikten sonra ve farklı tarayıcılarda tamamen anlamsız olabilir - ne olursa olsun uygulamanızda neyin yavaş olduğunu bulmak için profil oluşturmanız gerekir. Başka bir şeyi zaten optimize etmedikçe, tamsayı bölme yönteminiz olması olası değildir!
JonnyRaa

150

ES6 yeni Math.truncyöntemi tanıttı . Bu, @ MarkElliot'un cevabını negatif sayılar için de çalışmasını sağlamak için düzeltmeye izin verir :

var div = Math.trunc(y/x);
var rem = y % x;

MathYöntemlerin, 2 31 üzerindeki sayılarla çalıştıkları bitsel işleçlere göre avantajları olduğunu unutmayın .


var y = 18014398509481984; var x = 5; div =? - böcek?
4esn0k

4
@ 4esn0k Bu bir hata değil. Numaranız çok fazla basamak içeriyorsa, 64 bit ikili biçimdeki IEEE 754 numarasında bu kadar kesinliğe sahip olamazsınız. Örneğin 18014398509481984 == 18014398509481985,.
Oriol

18014398509481984 == 2 ** 54 ve ben bu sayıyı özellikle kullandım, çünkü tam olarak binary64 formatında temsil ediliyor. ve cevap da tam olarak temsil
ediliyor

1
Bence seçim basit: İmzalanmış 32 bite kadar numaralar için desteğe mi ihtiyacınız var? Kullanın ~~(x/y). 54 bite kadar imzalanmış daha büyük sayıları desteklemeniz mi gerekiyor? Math.truncElinizde varsa kullanın veya Math.floorbaşka bir şekilde (negatif sayılar için doğru). Daha büyük sayıları desteklemeniz mi gerekiyor? Bazı büyük sayı kitaplıkları kullanın.
Stijn de Witt

4
arama yapmak için google burada rubyists için divmod, bunu şöyle uygulayabilirsiniz:function divmod(x, y) { var div = Math.trunc(x/y); var rem = x % y; return [div, rem]; }
Alex Moore-Niemi

29
var remainder = x % y;
return (x - remainder) / y;

1
Bu sürüm maalesef x = -100 olduğunda testi geçemedi çünkü -33 yerine -34 döndürüyor.
Samuel

1
"var x = 0.3; var y = 0.01;" ? ( github.com/JuliaLang/julia/issues/4156#issuecomment-23324163 sayesinde )
4esn0k

Aslında, @Samuel, negatif değerlerle, bu yöntem doğru sonuçları döndürür veya en azından Math.trunc:) kullanan yöntemle aynı değerleri döndürür . 100,3 ile kontrol ettim; -100,3; 100, -3 ve -100, -3. Tabii ki, yorumunuz ve işler değiştikten sonra çok zaman geçti.
Marjan Venema

19

Normalde kullanıyorum:

const quotient =  (a - a % b) / b;
const remainder = a % b;

Muhtemelen en zarif değil, ama işe yarıyor.


1
Güzel bir çözümdür, çünkü bir şamandırayı ayrıştırmak veya kırpmaktan çirkinliği önler.
Dem Pilafian

6
hem bölüme hem de geri kalan kısmına ihtiyacınız varsa, önce geri kalanını hesaplayın ve sonra bu değeri bölümün ifadesinde tekrar kullanın, yani bölüm = (a - kalan) / b;
gb96

2
kalan = a% b; bölüm = (a - kalan) / b;
Zv_oDD

16

parseIntKesilmiş bir sonuç elde etmek için işlevi kullanabilirsiniz .

parseInt(a/b)

Geriye kalanları almak için mod operatörünü kullanın:

a%b

parseInt, taban 10 ile radix parametresini kullanmaktan kaçınmak için dizeleri olan bazı tuzaklar var

parseInt("09", 10)

Bazı durumlarda, sayının dize temsili bilimsel bir gösterim olabilir, bu durumda parseInt yanlış bir sonuç üretecektir.

parseInt(100000000000000000000000000000000, 10) // 1e+32

Bu çağrı sonuç olarak 1 üretecektir.


7
parseIntmümkün olduğunca kaçınılmalıdır. Douglas Crockford'un uyarısı şöyledir: "Dizenin ilk karakteri 0 ise, dize taban 10 yerine taban 8'de değerlendirilir. Taban 8, 8 ve 9'da rakamlar olmadığından parseInt (" 08 ") ve parseInt ("09") sonuç olarak 0 üretir.Bu hata, tarihleri ​​ve saatleri ayrıştıran programlarda sorunlara neden olur Neyse ki, parseInt bir radix parametresi alabilir, böylece parseInt ("08", 10) 8 üretir. radix parametresini girin. " archive.oreilly.com/pub/a/javascript/excerpts/…
Powers

3
Bir bölümde, bir dize değil, bir sayı almayı bekliyorum, ama bu iyi bir nokta.
Édipo Costa Rebouças

2
@ Güçleri bu yüzden sayı tabanı ekleyin. parseIntKaçınılması gerektiğini söylemez ; sadece farkında olmanız gereken bazı şeyler var. Bunların farkında olmalı ve başa çıkmaya hazır olmalısınız.
Hiçbiri

2
Asla parseIntbir sayı argümanı ile çağırma . parseIntkısmi sayı dizelerini ayrıştırmalı, kesik sayıları değil.
Oriol

1
İşlerin başlangıçta belirli bir şekilde kullanılması amaçlanmadığı için, yapmamanız gerektiği anlamına gelmez. Bu cevap işe yarıyor.
fregante

6

JavaScript, negatif sayıların tabanını ve tamsayı olmayan sayıların kalanını hesaplar ve matematiksel tanımları izler.

FLOOR, "parametreden daha küçük en büyük tam sayı" olarak tanımlanır, bu nedenle:

  • pozitif sayılar: FLOOR (X) = X'in tamsayı kısmı;
  • negatif sayılar: FLOOR (X) = X eksi 1'in tamsayı kısmı (çünkü parametreden KÜÇÜK olmalıdır, yani daha negatif!)

HATIRLATMA, bir bölümün (Öklid aritmetiği) "geride bırakılması" olarak tanımlanır. Temettü bir tamsayı olmadığında, bölüm genellikle bir tamsayı değildir, yani, kalan yoktur, ancak bölüm bir tamsayı olmaya zorlanırsa (ve birisi bir kişinin kalanını veya modülünü almaya çalıştığında bu olur kayan nokta sayısı), açık bir şekilde tamsayı olmayan bir "kalan" olacaktır.

JavaScript her şeyi beklendiği gibi hesaplar, bu nedenle programcı doğru soruları sormaya dikkat etmelidir (ve insanlar sorulan soruları cevaplamaya dikkat etmelidir!) Yarin'in ilk sorusu "X'in Y'nin tamsayı bölümü nedir" DEĞİLDİR, ancak, bunun yerine, "belirli bir tamsayının bir başkasına GİRİŞ sayısı". Pozitif sayılar için, cevap her ikisi için de aynıdır, ancak negatif sayılar için aynı değildir, çünkü tamsayı bölümü (bölenle temettü), bir sayının (bölen) "diğerine" (temettü) "gittiği zamandan -1 daha küçük olacaktır. Başka bir deyişle, FLOOR negatif bir sayının tamsayı bölümü için doğru cevabı döndürecektir, ancak Yarin bunu sormadı!

gammax doğru cevap verdi, bu kod Yarin tarafından istendiği gibi çalışıyor. Öte yandan Samuel yanılıyor, matematik yapmadı, sanırım ya da işe yaradığını görürdü (ayrıca, örneğinin böleninin ne olduğunu söylemedi, ama umarım öyleydi 3):

Kalan = X% Y = -100% 3 = -1

= (X - Kalan) / Y = (-100 - -1) / 3 = -99 / 3 = -33

Bu arada, Firefox 27.0.1'deki kodu test ettim, beklendiği gibi pozitif ve negatif sayılarla ve hem temettü hem de bölen için tamsayı olmayan değerlerle çalıştı. Misal:

-100.34 / 3.57: Gidiyor = -28, Kalan = -0.3800000000000079

Evet, fark ettim, orada hassas bir sorun var, ama kontrol etmek için zamanım olmadı (Firefox, Windows 7 veya CPU'mun FPU'su ile ilgili bir sorun olup olmadığını bilmiyorum). Ancak Yarin'in sadece tamsayıları içeren sorusu için gammax'ın kodu mükemmel çalışıyor.


5

Math.floor(operation) işlemin yuvarlanmış değerini döndürür.

1 Örnek st sorusuna:

var x = 5;
var y = 10.4;
var z = Math.floor(x + y);

console.log(z);

Konsol:

15

2. soru örneği :

var x = 14;
var y = 5;
var z = Math.floor(x%y);

console.log(x);

Konsol:

4


2

Sayfa sayısını hesaplama tek bir adımda yapılabilir: Math.ceil (x / y)


Bunun geri kalanını nasıl sağladığını anlamıyorum.
Paul Rooney

1

Alex Moore-Niemi'nin bir cevap olarak yorumu:

Burada arama yapan Google'dan Rubyistler için divmodbunu şu şekilde uygulayabilirsiniz:

function divmod(x, y) {
  var div = Math.trunc(x/y);
  var rem = x % y;
  return [div, rem];
}

Sonuç:

// [2, 33]

2
Genellikle, divmodkullanımları bölümü (katlı Math.floor(kesik bölünmesinden farklıdır), Math.trunc) negatif sayılar katılmaktadırlar. Bu, NPM divmodpaketi , Rubydivmod , SWI-Prologdivmod ve muhtemelen diğer birçok uygulama için de geçerlidir.
Palec

Kesik bölme, zeminli bölmeden daha doğal görünümlü sonuçlar verir, ancak uyumluluk bunu IMO'dan uzaklaştırır. Belki de yer döşemeli bölmeyi kullanmak için matematiksel veya performans nedenleri vardır. Genellikle, divmodiki işlemi ayrı ayrı hesaplamaktan iki kat daha hızlı gerçekleştirildiğinden emin olun. Bu performans avantajı olmadan böyle bir işlevin sağlanması kafa karıştırıcı olabilir.
Palec

1

Sadece iki güçle bölüyorsanız, bitsel operatörleri kullanabilirsiniz:

export function divideBy2(num) {
  return [num >> 1, num & 1];
}

export function divideBy4(num) {
  return [num >> 2, num & 3];
}

export function divideBy8(num) {
  return [num >> 3, num & 7];
}

(Birincisi bölüm, ikincisi kalan)


Genellikle function divideByPowerOf2(num, exponent) { return [num >> exponent, num & ((1 << exponent) - 1)]; },.
Palec

0

Pozitif ve negatif tamsayı değerlerinin nasıl ele alınacağına karar vermek için üçlü kullanabilirsiniz.

var myInt = (y > 0) ? Math.floor(y/x) : Math.floor(y/x) + 1

Sayı pozitifse, her şey yolunda demektir. Sayı negatifse, Math.floor negatifleri nasıl işlediğinden 1 ekler.


0

Bu her zaman sıfıra doğru kesilir. Çok geç olup olmadığından emin değilim, ama işte gidiyor:

function intdiv(dividend, divisor) { 
    divisor = divisor - divisor % 1;
    if (divisor == 0) throw new Error("division by zero");
    dividend = dividend - dividend % 1;
    var rem = dividend % divisor;
    return { 
        remainder: rem, 
        quotient: (dividend - rem) / divisor
    };
}

0

JS çalışma zamanının böyle gösteremediği çok büyük tamsayıların kalanını hesaplamanız gerekiyorsa (2 ^ 32'den büyük herhangi bir tam sayı bir kayan nokta olarak gösterilir ve bu nedenle kesinlik kaybeder), biraz hile yapmanız gerekir.

Bu, günlük hayatımızın birçok örneğinde (banka hesap numaraları, kredi kartları, ...) bulunan birçok kontrol basamağı durumunu kontrol etmek için özellikle önemlidir.

Her şeyden önce numaranızı bir dize olarak ihtiyacınız var (aksi takdirde zaten kesinlik kaybettiniz ve geri kalanı mantıklı değil).

str = '123456789123456789123456789'

Artık dizenizi, daha küçük parçalara ayırmanız gerekir, böylece kalanların ve bir dizenin birleştirilmesi 9 haneye sığabilir.

digits = 9 - String(divisor).length

Dizeyi bölmek için normal bir ifade hazırlama

splitter = new RegExp(`.{1,${digits}}(?=(.{${digits}})+$)`, 'g')

Örneğin digits, 7 ise normal ifade

/.{1,7}(?=(.{7})+$)/g

Maksimum uzunluğu 7 olan boş olmayan bir alt dize ile eşleşir (bunu (?=...)pozitif bir ileriye doğru) ve 7'den daha fazla sayıda karakter takip eder. 'G' ifadesi, ilk eşleşmede durmadan tüm dizgiden geçmesini sağlamaktır.

Şimdi her bir parçayı tamsayıya dönüştürün ve kalanları hesaplayın reduce(önceki geri kalanı - veya 0 - 10'un doğru gücüyle çarparak):

reducer = (rem, piece) => (rem * Math.pow(10, digits) + piece) % divisor

Bu, "çıkarma" kalan algoritması nedeniyle çalışacaktır:

n mod d = (n - kd) mod d

bu, son sayıyı etkilemeden bir sayının ondalık gösteriminin herhangi bir 'başlangıç ​​kısmının' geri kalanı ile değiştirilmesine izin verir.

Son kod şöyle görünecektir:

function remainder(num, div) {
  const digits = 9 - String(div).length;
  const splitter = new RegExp(`.{1,${digits}}(?=(.{${digits}})+$)`, 'g');
  const mult = Math.pow(10, digits);
  const reducer = (rem, piece) => (rem * mult + piece) % div;

  return str.match(splitter).map(Number).reduce(reducer, 0);
}
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.