Tamsayıyı 2'ye bölmek için hangisi daha iyi bir seçenek?


406

Bir tamsayıyı 2'ye bölmek için aşağıdaki tekniklerden hangisi en iyi seçenektir ve neden?

Teknik 1:

x = x >> 1;

Teknik 2:

x = x / 2;

İşte xbir tamsayı.


75
Sonucu gerçekten xyeniden atamak istiyorsanız , ikisi de bu şekilde uygun değildir: işlemle neyi ifade etmek istediğinize bağlı olarak ya x >>= 1ya olmalıdır x /= 2. Daha hızlı olduğu için değil (herhangi bir modern derleyici, eşdeğer varyantları yine de aynı, hızlı montajla derleyecektir), ancak daha az kafa karıştırıcı olduğu için.
leftaroundabout

33
Artık ortama katılmıyorum. - Ama bence birçok programlama dilinde aritmetik kaydırma adı verilen ve işaret bitini yerinde tutan ve bu nedenle işaretli değerler için beklendiği gibi çalışan bir işlem olması dikkat çekicidir . Sözdizimi şöyle olabilir x = x >>> 1. Ayrıca platforma ve derleyiciye bağlı olarak, vardiyaları kullanarak bölümleri ve çarpmaları manuel olarak optimize etmenin oldukça makul olabileceğini unutmayın. - Mikro kontrolörlerin düşünülmesi, örneğin çarpma için doğrudan ALU desteği olmadan.
JimmyB

36
Tercih ederim x /= 2çünkü x >>= 1monadik bağlama çok benziyor;)
fredoverflow 22:12

19
@ leftaroundabout - Bunun x = x / 2yerine yazmak için çok daha okunabilir olduğunu düşünüyorum x /= 2. Öznel tercih belki :)
JimmyB

8
@HannoBinder: kesinlikle öznel, özellikle çok alışkanlık. IMO, tüm aritmetik işleçlerin ⬜=kombinasyonları olduğu bir dilde , mümkün olduğunda bunlar kullanılmalıdır. Bu gerçeği gürültü ve koyar vurgu kaldırır xedilir modifiye genel ederken, =operatörün daha çok eskisinin tamamen yeni bir değer bağımsız alır düşündürmektedir. - Her zaman yanı onun nokta olabilir, ama o zaman son derece yararlı vazgeçmek gerekiyordu (sadece matematiksel operatörleri bilen birisi yani okunabilir yüzden o) kombine operatörleri kaçınarak ++, --, +=de.
leftaroundabout

Yanıtlar:


847

Ne yapmaya çalıştığınızı en iyi açıklayan işlemi kullanın.

  • Numaraya bir bit dizisi olarak davranıyorsanız, bit kaydırma tuşunu kullanın.
  • Eğer bunu sayısal bir değer olarak görüyorsanız bölümü kullanın.

Bunların tam olarak eşdeğer olmadığını unutmayın. Negatif tamsayılar için farklı sonuçlar verebilirler. Örneğin:

-5 / 2  = -2
-5 >> 1 = -3

(İdeone)


20
Orijinal soru 'en iyi' terimi hakkında da belirsizdi. Hız, okunabilirlik, öğrencileri kandırmak için sınav sorusu, vb. Bakımından 'en iyi' ... 'En iyi' ne demek olduğu konusunda bir açıklama olmaması durumunda, bu en doğru cevap gibi görünüyor.
Ray

47
C ++ 03, her iki negatif sayılar için tanımlı uygulama vardır ve belki aynı sonuçları verir. C ++ 11'de, bölme negatif sayılar için iyi tanımlanmıştır, ancak kaydırma yine de uygulama olarak tanımlanmıştır.
James Kanze

2
/ İfadesinin uygulaması erken C standartlarında tanımlanan (negatif sayılar için yukarı veya aşağı yuvarlanıyorsa) tanımıdır. Her zaman% ile uyumlu olmalıdır (modulo / kalan operatör).
ctrl-alt-delor

7
"Uygulama tanımlı", derleyicinin uygulayıcının, genellikle önemli kısıtlamalarla çeşitli uygulama seçenekleri arasından seçim yapması gerektiği anlamına gelir. Burada, bir kısıt olduğunu %ve /böylece operatörler hem pozitif hem negatif işlenenler için tutarlı olmalıdır (a/b)*b+(a%b)==abakılmaksızın belirtileri de geçerlidir ave b. Genellikle, yazar CPU'dan mümkün olan en iyi performansı elde etmek için seçimler yapar.
RBerteig

6
"Derleyici zaten bir vardiyaya dönüştürecek" diyen herkes yanlış, değil mi? Derleyici, negatif olmayan bir tamsayı ile uğraştığınızı garanti edemezse (ya sabittir ya da işaretsiz bir int'dir), bir vardiyaya değiştiremez
Kip

225

İlki bölünmeye benziyor mu? Hayır. Bölmek istiyorsanız, kullanın x / 2. Derleyici, mümkünse bit kaydırmayı kullanmak için optimize edebilir (buna güç azaltma denir), bu da kendi başınıza yaparsanız işe yaramaz bir mikro optimizasyon yapar.


15
Birçok derleyici, ikisinin gücü ile bölünmeyi bit kaydırmaya dönüştürmez. İşaretli tamsayılar için yanlış bir optimizasyon olurdu. Derleyicinizden montaj çıktısına bakmaya ve kendiniz görmeye çalışmalısınız.
exDM69

1
IIRC Bunu CUDA'da paralel azaltmayı daha hızlı yapmak için kullandım (tamsayı bölmekten kaçının). Ancak bu bir yıldan uzun bir süre önceydi, bugünlerde CUDA derleyicilerinin ne kadar akıllı olduğunu merak ediyorum.
Nils

9
@ exDM69: Birçok derleyici bunu imzalı tamsayılar için bile yapar ve bunları sadece imzalı göre ayarlar. Bu şeylerle oynamak için güzel bir araç şudur: tinyurl.com/6uww253
PlasmaHH

19
@ exDM69: Ve bu alakalı, nasıl? "Mümkünse" dedim, "her zaman" değil. Optimizasyon yanlışsa, bunu manuel olarak yapmak doğru yapmaz (ayrıca belirtildiği gibi GCC, imzalı tamsayıların doğru değiştirilmesini anlayacak kadar akıllıdır).
Cat Plus Plus

4
WikiPedia sayfasına bakıldığında, bu tartışmalı bir konudur, ancak buna bir güç azaltımı demezdim. Bir döngüde, döngüdeki önceki değerlere ekleyerek, bir döngüde, örneğin, çarpma işleminden çarpma işlemine düştüğünüz zaman bir güç azalmasıdır. Bu, derleyicilerin oldukça güvenilir bir şekilde yapabileceği bir gözetleme deliği optimizasyonudur.
SomeCallMeTim

189

Yığmak için: kullanmayı tercih etmek için birçok neden var x = x / 2; İşte bazıları:

  • niyetinizi daha açık bir şekilde ifade eder (biraz dönen kayıt bitleri veya başka bir şeyle uğraşmadığınızı varsayarak)

  • derleyici bunu zaten bir vardiya işlemine indirgeyecek

  • derleyici onu azaltmasa ve vardiyadan daha yavaş bir işlem seçse bile, bunun programınızın performansını ölçülebilir bir şekilde etkileme olasılığı kaybolur (ve ölçülebilir şekilde etkiliyorsa) vardiya kullanma nedeni)

  • bölüm daha büyük bir ifadenin parçası olacaksa, bölüm operatörünü kullanırsanız önceliği doğru elde etme olasılığınız daha yüksektir:

    x = x / 2 + 5;
    x = x >> 1 + 5;  // not the same as above
  • imzalı aritmetik, yukarıda belirtilen öncelik sorunundan daha da karmaşık olabilir

  • Tekrarlamak gerekirse - derleyici zaten bunu sizin için yapacak. Aslında, bölünmeyi bir sabitle, sadece ikisinin güçlerine değil, her türlü sayı için bir dizi vardiyaya, eklemeye ve çarpmaya dönüştürecektir. Bununla ilgili daha fazla bilgi için bu soruya bakın .

Kısacası, gerçekten çoğaltmak veya bölmek istediğinizde bir vardiya kodlayarak hiçbir şey satın almazsınız, ancak belki de bir hata getirme olasılığı artar. Derleyiciler uygun olduğunda bu tür bir şeyi optimize etmek için yeterince akıllı olmadıkları için bir ömür oldu.


5
Öncelik kuralları varken, parantez kullanmanın yanlış bir şey olmadığını da eklemeye değer. Bazı üretim kodunu yenilerken, aslında çok daha okunabilir yerine formun a/b/c*d( a..dsayısal değişkenler belirtildi) bir şey gördüm (a*d)/(b*c).

1
Performans ve optimizasyonlar derleyiciye ve hedefe bağlıdır. Örneğin, ticari derleyiciyi satın almadığınız sürece -O0'dan daha yüksek herhangi bir şeyin devre dışı bırakıldığı bir mikrodenetleyici için biraz çalışıyorum, bu yüzden derleyici kesinlikle bitshift'e bölünmeyecek. Ayrıca, bit kaydırmaları bir döngü alır ve bölme bu hedef üzerinde 18 döngü alır ve mikrodenetleyicilerin saat hızı oldukça düşük olduğundan, bu gerçekten göze çarpan bir performans isabeti olabilir (ancak kodunuza bağlıdır - profilleme size söyleyene kadar kesinlikle / kullanmalısınız. bu bir sorun!)

4
@JackManey, taşma üretme a*dveya b*ctaşma olasılığı varsa , daha az okunabilir form eşdeğer değildir ve belirgin bir avantajı vardır. Not: Parantezlerin en iyi arkadaşınız olduğunu kabul ediyorum.
Mark Ransom

@MarkRansom - Adil bir nokta ( a/b/c*dR kodunda karşılaşmış olmama rağmen - taşmanın bir şey verilerle ciddi bir şekilde yanlış olduğu anlamına gelir - örneğin, C kodu performans açısından kritik bir blokta değil).

Kod x=x/2;daha sadece "net" olduğunu x>>=1eğer xbir tek negatif sayı asla veya bir yaklaşık off-birer hataları umursamıyor. Aksi halde x=x/2;ve x>>=1;farklı anlamlar taşır. İhtiyaç duyduğu şey, hesaplanan değer ise x>>=1, bunu iki x = (x & ~1)/2veya daha fazla x = (x < 0) ? (x-1)/2 : x/2bölmeyi kullanmayı düşünebileceğimden daha açık veya başka bir formülasyon olarak görecektim . Aynı şekilde, kişinin hesapladığı değere ihtiyacı varsa x/=2, bu daha nettir ((x + ((unsigned)x>>31)>>1).
supercat

62

Hangisi en iyi seçenektir ve neden tamsayı sayısını 2'ye bölmek için?

En iyi ne demek istediğinize bağlı .

İş arkadaşlarınızın sizden nefret etmesini veya kodunuzun okunmasını zorlaştırmak istiyorsanız, kesinlikle ilk seçenekle devam edeceğim.

Bir sayıyı 2'ye bölmek istiyorsanız, ikincisine gidin.

İkisi eşdeğer değildir, sayı negatifse veya daha büyük ifadelerin içinde ise aynı şekilde davranmazlar - bitshift'in önceliği daha düşüktür +veya- bölümün yüksek önceliğe sahiptir.

Niyetini ifade etmek için kodunuzu yazmalısınız. Performans endişenizse endişelenmeyin, optimize edici bu tür mikro optimizasyonlarda iyi bir iş çıkarır.


58

Daha /açık olduğunu varsayarak, divide ( ) kullanın . Derleyici buna göre optimize eder.


34
Derleyici buna göre optimizasyon yapmalıdır .
Noctis Skytower

12
Derleyici değil optimize buna göre yaparsa, sen gerektiğini daha iyi bir derleyici kullanın.
David Stone

3
@DavidStone: Bir derleyici, bir negatif kadar işaretli tamsayının 1'den başka herhangi bir sabitle bölünmesini, bir vardiya kadar verimli olacak şekilde optimize edebilir mi?
supercat

1
@supercat: Bu iyi bir nokta. Tabii ki değeri imzasız bir tamsayıda saklayabilirsiniz (imzalı / imzasız uyumsuzluk uyarıları ile birleştirildiğinde olması gerekenden çok daha kötü bir şöhrete sahip olduğumu hissediyorum) ve çoğu derleyici de optimize ederken bir şeylerin doğru olduğunu varsaymalarını söyleme yoluna sahip . O sarma tercih bir uyumluluk makroda ve böyle bir şey olurdu ASSUME(x >= 0); x /= 2;üzerinde x >>= 1;, ama hala getirmek için önemli bir noktadır.
David Stone

39

Tercih etmeniz gereken diğer cevaplara katılıyorum x / 2 çünkü amacı daha açık ve derleyici bunu sizin için optimize etmeli.

Bununla birlikte, tercih x / 2edilmesinin bir başka nedeni x >> 1de >>, ximzalı intve negatifse davranışının uygulamaya bağlı olmasıdır.

Bölüm 6.5.7, ISO C99 standardının madde işareti 5'inden:

Sonucu E1 >> E2olduğu E1sağ kaymıştır E2bit pozisyonları. Eğer E1imzalanmamış türü olan veya eğer E1imzalı türünü ve negatif olmayan bir değeri vardır, sonucun değeri olan bölümün parçasıdır E1/ '2 E2. Eğer E1imzalı türünü ve negatif değere sahiptir, ortaya çıkan değer uygulaması tanımlı olduğunu.


3
x>>scalepowerNegatif sayılar üzerinde birçok uygulamanın tanımladığı davranışın, bir değeri ekran oluşturma gibi amaçlar için iki güce böldüğünde tam olarak gerekli olan davranış olacağını, buna ek x/scalefactorolarak, negatif değerlere düzeltmeler uygulanmadığı sürece kullanımın yanlış olacağını belirtmek gerekir.
supercat

32

x / 2daha nettir ve x >> 1çok daha hızlı değildir (mikro karşılaştırmaya göre, bir Java JVM için yaklaşık% 30 daha hızlıdır). Diğerlerinin de belirttiği gibi, negatif sayılar için yuvarlama biraz farklıdır, bu nedenle negatif sayıları işlemek istediğinizde bunu dikkate almalısınız. Bazı derleyiciler otomatik x / 2olarakx >> 1 onlar sayı negatif olamaz biliyorum (hatta bunu doğrulamak olamazdı düşünce).

Bazı kısayollar mümkünx / 2 olduğu için (yavaş) bölünmüş CPU komutunu bile kullanamayabilir , ancak yine de daha yavaştır .x >> 1

(Bu soru, diğer programlama dilleri daha çok operatörün ++ C / C'dir. Java için işaretsiz sağa kaydırma da var, x >>> 1yine farklı olan. O yüzden doğru iki değerin ortalaması (ortalama) değerini hesaplamak için izin verir (a + b) >>> 1irade çok büyük değerleri için bile ortalama değer döndürmek ave b. dizi endeksleri çok büyük olabilir, bu ikili arama için örneğin gereklidir. Orada ikili aramanın birçok sürümlerinde bir hata , kullandıkları çünkü (a + b) / 2ortalama hesaplamak için. Bu etmiyor doğru çalışmıyor. Bunun (a + b) >>> 1yerine doğru çözüm kullanmaktır .)


1
Derleyiciler dönüştürülemiyor x/2için x>>1olgularda xnegatif olabilir. Eğer birinin istediği x>>1hesaplanacak değer ise , bu kesinlikle x/2aynı değeri hesaplayan herhangi bir ifadeden kesinlikle daha hızlı olacaktır .
supercat

Haklısın. Bir derleyiciler sadece dönüştürebilir x/2için x>>1o değeri biliyorsa negatif değil. Cevabımı güncellemeye çalışacağım.
Thomas Mueller

derleyiciler hala önleyebilirim divdönüştürerek olsa talimat x/2içine (x + (x<0?1:0)) >> 1(burada >> o işareti bit kaymalar bir aritmetik sağ kaymasıdır). Bu 4 talimat alır: değeri kopyalayın, shr (bir reg'deki sadece işaret bitini almak için), ekleyin, sar. goo.gl/4F8Ms4
Peter Cordes

Soru C ve C ++ olarak etiketlenmiştir.
Josh Sanford

22

Knuth şöyle dedi:

Erken optimizasyon tüm kötülüklerin köküdür.

Bu yüzden kullanmanızı öneririm x /= 2;

Bu şekilde kodun anlaşılması kolaydır ve bu işlemin bu formda optimizasyonunun işlemci için büyük bir fark anlamına gelmediğini düşünüyorum.


4
Tamsayıların (doğal sayılar ve gerçek sayılar için geçerli) aksiyomu (n + d) / d = (n / d) + 1? Grafikleri ölçeklerken aksiyomun ihlali, sonuçta görünür "dikişlere" neden olur. Eğer kişi eşit ve neredeyse sıfır etrafında simetrik bir şey isterse, (n+8)>>4güzel çalışır. İmzalı bir doğru vardiya kullanmadan açık veya verimli bir yaklaşım sunabilir misiniz?
supercat

19

Karar vermenize yardımcı olması için derleyici çıktısına bir göz atın. Bu testi x86-64 üzerinde
gcc (GCC) 4.2.1 20070719 [FreeBSD] ile çalıştırdım

Ayrıca godbolt'ta çevrimiçi derleyici çıktılarına bakın .

Gördüğünüz şey, derleyicinin sarlher iki durumda da (aritmetik sağ kaydırma) komutunu kullanmasıdır, bu nedenle iki ifade arasındaki benzerliği tanır. Bölmeyi kullanırsanız, derleyicinin negatif sayıları da ayarlaması gerekir. Bunu yapmak için işaret bitini en düşük sıra bitine kaydırır ve bunu sonuca ekler. Bu, negatif sayıları değiştirirken bir bölmenin ne yapacağına kıyasla tek tek sorunu giderir.
Bölme durumu 2 vardiya yaptığından, açık vardiya durumu sadece bir tane yaparken, şimdi burada diğer cevaplarla ölçülen bazı performans farklılıklarını açıklayabiliriz.

Montaj çıkışlı C kodu:

Bölmek için, girdiniz

int div2signed(int a) {
  return a / 2;
}

ve bu derleme

    movl    %edi, %eax
    shrl    $31, %eax
    addl    %edi, %eax
    sarl    %eax
    ret

vardiya için benzer şekilde

int shr2signed(int a) {
  return a >> 1;
}

çıktı ile:

    sarl    %edi
    movl    %edi, %eax
    ret

Biri ne yaptığını bağlı olarak, kapalı-birer hatayı düzeltmek olabilir veya olabilir neden bunu düzeltmek için daha fazla kodunun kullanımını gerektirecektir (aslında gerekli neyin ile karşılaştırıldığında) bir kapalı-tek hatası. Eğer birinin istediği yer döşenmiş bir sonuçsa, sağa kaydırma, bildiğim herhangi bir alternatiften daha hızlı ve daha kolay olur.
supercat

Bir zemine ihtiyacınız varsa, "2'ye bölme" olarak istediğinizi tanımlamanız pek olası değildir
Michael Donohue

Hem doğal sayıların hem de reel sayıların bölünmesi (n + d) / d = (n / d) +1 aksiyomunu destekler. Reel sayıların bölünmesi, doğal sayılarla anlamsız olan bir aksiyom olan (-n) / d = - (n / d) 'yi de destekler. Tamsayılarda kapalı olan ve her iki aksiyomu da destekleyen bir bölme operatörüne sahip olmak mümkün değildir. Aklıma göre, ilk aksiyomun tüm sayılar için, ikincisi ise sadece gerçekler için tutulması gerektiğini söylemek, ilkinin tamsayılar veya gerçekler için tutması gerektiğini söylemek, tamsayılar için değil. Ayrıca, ikinci aksiyomun hangi durumlarda gerçekten yararlı olduğunu merak ediyorum .
Supercat

1
İlk aksiyomu karşılayan bir tamsayı bölme yöntemi, sayı çizgisini boyut bölgelerine böler d. Bu tür bölümleme birçok amaç için yararlıdır. Birinin kesme noktası 0 ile -1 arasında bir yerde olsa bile, bir ofset eklemek onu hareket ettirir. İkinci aksiyomu tatmin eden bir tamsayı bölümü, sayı çizgisini çoğunlukla büyük olan d, ancak biri büyük olan bölgelere böler 2*d-1. Tam olarak "eşit" bölünmeler değil. Oddball bölümünün gerçekten ne zaman yararlı olduğuna dair öneriler sunabilir misiniz?
supercat

Shr2signed için derleyici çıktınız yanlış. gcc x86 üzerinde aritmetik kaymalar ( sar) ile >> imzalı tamsayıları uygulamayı seçer . goo.gl/KRgIkb . Bu posta listesi gönderisi ( gcc.gnu.org/ml/gcc/2000-04/msg00152.html ), gcc'nin geçmişte imzalı girişler için aritmetik vardiya kullandığını doğrular, bu nedenle FreeBSD gcc 4.2.1'in imzasız vardiya kullanması pek olası değildir. Bunu düzeltmek için yazınızı güncelledim ve her ikisinin de kullandıkları SAR olduğunda ikisinin de shr kullandığını söyleyen ilk paragrafı güncelledim. SHR, /durum için işaret bitini nasıl ayıkladığıdır . Ayrıca bir godbolt bağlantısı da vardı.
Peter Cordes

15

Sadece eklenen bir not -

Bazı VM tabanlı dillerde x * = 0.5 genellikle daha hızlı olacaktır - özellikle de actioncript, değişkenin 0'a bölünmesi için kontrol edilmesi gerekmeyeceğinden.


2
@minitech: Bu çok kötü bir test. Testteki tüm kod sabittir. Kod JIT olmadan bile tüm sabitleri ortadan kaldırır.

@ M28: jsPerf'in içlerinin (yani eval) bunu her seferinde yeniden yarattığından emindim . Ne olursa olsun, evet, oldukça kötü bir test, çünkü çok aptalca bir optimizasyon.
Ry-

13

x = x / 2; VEYA kullanın x /= 2;Gelecekte yeni bir programcının üzerinde çalışması mümkündür. Bu yüzden kod satırında neler olduğunu öğrenmesi daha kolay olacaktır. Herkes bu tür optimizasyonların farkında olmayabilir.


12

Yarışmaların programlanması amacıyla söylüyorum. Genellikle, 2'ye bölünmenin birçok kez gerçekleştiği ve girdinin pozitif veya negatif olduğu bilinen çok büyük girdilere sahiptir.

x >> 1, x / 2'den daha iyi olacaktır. 2 işlemle 10 ^ 10'dan fazla bölümün gerçekleştiği bir program çalıştırarak ideone.com'u kontrol ettim. x / 2 yaklaşık 5.5 saniye sürerken x >> 1 aynı program için yaklaşık 2.6 saniye aldı.


1
İşaretsiz değerler için, bir derleyici optimize etmelidir x/2etmek x>>1. İmzalı değerler için, neredeyse tüm uygulamalar , pozitif olduğunda ve daha hızlı olduğu zaman negatif olandan daha hızlı hesaplanabilen x>>1bir anlama sahip olmayı tanımlar . x/2xx/2x
supercat

12

Dikkate alınması gereken birkaç şey olduğunu söyleyebilirim.

  1. Bitshift daha hızlı olmalıdır, çünkü bitleri kaydırmak için gerçekten özel bir hesaplama gerekmez, ancak belirtildiği gibi, negatif sayılarla ilgili potansiyel sorunlar vardır. Pozitif sayılara sahip olduğunuzdan emin değilseniz ve hız arıyorsanız, bitshift'i tavsiye ederim.

  2. Bölme operatörü, insanların okuması için çok kolaydır. Kod okunabilirliği arıyorsanız, bunu kullanabilirsiniz. Derleyici optimizasyonu alanının uzun bir yol kat ettiğine dikkat edin, bu nedenle kodun okunmasını ve anlaşılmasını kolaylaştırmak iyi bir uygulamadır.

  3. Temel donanıma bağlı olarak, işlemlerin farklı hızları olabilir. Amdal'ın yasası, ortak davayı hızlı hale getirmek. Böylece farklı işlemleri diğerlerinden daha hızlı gerçekleştirebilen donanımlarınız olabilir. Örneğin, 0,5 ile çarpmak 2'ye bölmekten daha hızlı olabilir. (Eğer tamsayı bölünmesini zorunlu kılmak istiyorsanız çarpmanın tabanını almanız gerekebilir).

Saf performans peşindeyseniz, işlemleri milyonlarca kez yapabileceğiniz bazı testler oluşturmanızı öneririm. OS / Donanım / Derleyici / Kodunuzla hangisinin istatistiksel olarak en iyi olduğunu belirlemek için yürütmeyi birkaç kez örnekleyin (örnek boyutunuz).


2
Msgstr "Bitshift daha hızlı olmalı". derleyiciler bölümleri bitlere dönüştürecek
Trevor Hickey

Umarım derlerdi, ancak derleyicinin kaynağına erişiminiz yoksa emin olamazsınız :)
James Oravec

1
Bir kişinin uygulaması en yaygın şekilde ele alıyorsa ve negatif sayılarla ne yapmak >>ve ne uyuşmadığıyla eşleşmek istediği şekilde bitshift'i de öneririm /.
supercat

12

CPU ile ilgili olarak, bit kaydırma işlemleri bölme işlemlerinden daha hızlıdır. Ancak, derleyici bunu bilir ve olabildiğince uygun şekilde optimize eder, böylece kodunuzun verimli çalıştığını bilerek en mantıklı ve dinlendirici bir şekilde kodlayabilirsiniz. Ancak bir unsigned intkutunun (bazı durumlarda) daha intönce işaret edilen nedenlerden birinden daha iyi optimize edildiğini unutmayın . İmzalı aritmatik ihtiyacınız yoksa, işaret bitini eklemeyin.


11

x = x / 2; kullanmak için uygun koddur .. ancak bir işlem, kendi programınıza üretmek istediğiniz çıktıya bağlıdır.


11

Niyetlerinizi daha net hale getirin ... örneğin, bölmek istiyorsanız, x / 2 kullanın ve derleyicinin operatörü (veya başka bir şeyi) kaydırmak için optimize etmesine izin verin.

Günümüzün işlemcileri, bu optimizasyonların programlarınızın performansı üzerinde herhangi bir etkiye sahip olmasına izin vermeyecektir.


10

Bunun cevabı altında çalıştığınız ortama bağlı olacaktır.

  • 8 bitlik bir mikrodenetleyici üzerinde veya çarpma için donanım desteği olmayan bir şey üzerinde çalışıyorsanız, bit değişimi beklenir ve yaygındır ve derleyici neredeyse kesinlikle x /= 2dönüşürken x >>= 1, bir bölme sembolünün varlığı o ortamda daha fazla kaş yükseltir bir bölümü etkilemek için bir vardiya kullanma.
  • Performans açısından kritik bir ortamda veya kod bölümünde çalışıyorsanız veya kodunuz derleyici optimizasyonu kapalı olarak derlenebilirse, x >>= 1bunun nedenini açıklamak için muhtemelen en iyisi açıktır.
  • Yukarıdaki koşullardan biri altında değilseniz, kodunuzu sadece kullanarak daha okunabilir hale getirin x /= 2. Kodunuza bakmakta olan bir sonraki programcıyı, vardiya işleminizde 10 saniyelik iki kere almaktan daha iyi, kaymanın daha verimli sans derleyici optimizasyonu olduğunu bildiğinizi kanıtlamaktan daha iyidir.

Bütün bunlar imzasız tamsayıları varsayar. Basit geçiş, muhtemelen imzalamak için istediğiniz şey değildir. Ayrıca, DanielH, x *= 0.5ActionScript gibi belirli diller için kullanma konusunda iyi bir nokta getiriyor .


8

mod 2, = 1 için test edin. c sözdizimini bilin. ama bu en hızlı olabilir.


7

genellikle sağ kayma bölünür:

q = i >> n; is the same as: q = i / 2**n;

bu bazen programları netlik pahasına hızlandırmak için kullanılır. Yapman gerektiğini sanmıyorum. Derleyici, hızlandırmayı otomatik olarak gerçekleştirecek kadar akıllıdır. Bu , bir vardiyaya girmenin netlik pahasına size hiçbir şey kazandırmayacağı anlamına gelir .

Pratik C ++ Programlama'dan bu sayfaya bir göz atın .


Eğer kişi , maksimuma yakın olmayan (x+128)>>8değerler için hesaplanacak olan değeri hesaplamak isterse, xbir kayma olmadan bunu özlü bir şekilde nasıl yapabilir? Not (x+128)/256not çalışma. Güzel bir ifade biliyor musun?
supercat

7

Açıkçası, kodunuzu okuyan bir sonraki adam için yazıyorsanız, "x / 2" nin netliğine gidin.

Ancak, hız hedefinizse, hem sonuçları hem de zamanı deneyin. Birkaç ay önce, bir tamsayı dizisine adım atmayı ve her öğeyi 2'ye bölmeyi içeren bir bitmap evrişim rutini üzerinde çalıştım. "X" 1 "yerine" x "yerine eski hileyi de dahil etmek için her türlü şeyi yaptım / 2" .

Aslında her iki şekilde zamanladı zaman x / 2 x daha hızlı olduğunu şaşırdım keşfetti >> 1

Bu, varsayılan optimizasyonlar açıkken Microsoft VS2008 C ++ kullanıyordu.


4

Performans açısından. CPU'nun kaydırma işlemleri bölme op kodlarından önemli ölçüde daha hızlıdır. Yani ikiye bölmek veya 2 vb. İle çarpmak vardiya işlemlerinden faydalanır.

Görünüm ve his açısından. Mühendis olarak ne zaman kozmetik ürünlere o kadar bağlı olduk ki, güzel bayanlar bile kullanmıyor! :)


3

X / Y doğru olanıdır ... ve ">>" kaydırma operatörüdür. shift operatörü bitleri kaydırmak için kullanılır.

x = x / 2; x / = 2; böyle kullanabiliriz ..


0

X >> 1, x / 2'den daha hızlı olsa da, negatif değerlerle uğraşırken >> 'nin doğru kullanımı biraz daha karmaşıktır. Aşağıdakine benzer bir şey gerektirir:

// Extension Method
public static class Global {
    public static int ShiftDivBy2(this int x) {
        return (x < 0 ? x + 1 : x) >> 1;
    }
}
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.