Aritmetik Taşma neden ihmal edilir?


76

Hiç favori programlama dilinizdeki 1 ila 2.000.000 arasındaki tüm sayıları toplamaya çalıştınız mı? Sonuç elle hesaplamak kolaydır: işaretsiz bir 32bit tamsayının maksimum değerinden 900 kat daha büyük olan 2,000,001,000,000.

C # yazdırır -1453759936- negatif bir değer! Ve sanırım Java da aynı şeyi yapıyor.

Bu, varsayılan olarak Aritmetik Taşma'yı görmezden gelen bazı ortak programlama dilleri olduğu anlamına gelir (C # dilinde, bunu değiştirmek için gizli seçenekler vardır). Bu benim için çok riskli görünen bir davranış ve bu tür bir taşma nedeniyle Ariane 5'in çöküşü değil miydi?

Öyleyse: Böylesine tehlikeli bir davranışın arkasındaki tasarım kararları nelerdir?

Düzenle:

Bu sorunun ilk cevapları, aşırı kontrol maliyetlerini ifade ediyor. Bu varsayımı test etmek için kısa bir C # programı çalıştıralım:

Stopwatch watch = Stopwatch.StartNew();
checked
{
    for (int i = 0; i < 200000; i++)
    {
        int sum = 0;
        for (int j = 1; j < 50000; j++)
        {
            sum += j;
        }
    }
}
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);

Makinemde işaretli sürüm 11015ms, denetlenmeyen sürüm 4125ms. Diğer bir deyişle, kontrol adımları numaraların eklenmesiyle neredeyse iki katına çıkar (toplamda orijinal sürenin 3 katı). Ancak 10.000.000.000 tekrarla, çekle geçen zaman hala 1 nanosaniyeden az. Bunun önemli olduğu bir durum olabilir, ancak çoğu uygulama için bunun önemi yoktur.

Düzenleme 2:

Sunucu uygulamamızı (birkaç sensörden gelen verilerin analizini yapan bir Windows servisi, oldukça fazla sayıda kırılmaya neden oldu) /p:CheckForOverflowUnderflow="false"parametre ile (normalde taşma kontrolünü açtım) yeniden derledim ve bir cihaza yerleştirdim. Nagios izleme, ortalama CPU yükünün% 17'de kaldığını gösteriyor.

Bu, yukarıdaki telafi örneğinde bulunan performans isabetinin uygulamamızla tamamen alakasız olduğu anlamına gelir.


19
Tıpkı bir not olarak, C # için checked { }, kodun Aritmetik Taşma kontrollerini yapması gereken bölümlerini işaretlemek için bölümü kullanabilirsiniz . Bu performans nedeniyle
Paweł asukasik

14
"En sevdiğiniz programlama dilinde 1 ila 2.000.000 arası tüm sayıları toplamayı denediniz mi?" - Evet: (1..2_000_000).sum #=> 2000001000000. Benim favori dillerden biri daha: sum [1 .. 2000000] --=> 2000001000000. Benim favori: Array.from({length: 2000001}, (v, k) => k).reduce((acc, el) => acc + el) //=> 2000001000000. (Adil olmak gerekirse, sonuncusu hile yapıyor.)
Jörg W Mittag

27
IntegerHaskell'deki @BernhardHiller isteğe bağlı olarak duyarlıdır , ayrılabilir RAM'lerin bitmediği sürece herhangi bir sayı tutacaktır.
Polygnome

50
Ariane 5 kazasında önemli olmayan bir taşma kontrolü yapıldı - roket, bir uçuş sonucunun daha da gerekli olmadığı bir uçağın içindeydi. Bunun yerine taşma tespit edildi ve bu da uçuşun iptal edilmesine neden oldu.
Simon B,

9
But with the 10,000,000,000 repetitions, the time taken by a check is still less than 1 nanosecond.Bu, optimize edilen döngünün bir göstergesidir. Ayrıca bu cümle benim için çok geçerli görünen önceki rakamlarla çelişiyor.
usr

Yanıtlar:


86

Bunun 3 nedeni var:

  1. Çalışma süresinde taşma kontrolü (her bir aritmetik işlem için) fazladır.

  2. Derleme zamanında bir taşma kontrolünün atlanabileceğini kanıtlamanın karmaşıklığı aşırıdır.

  3. Bazı durumlarda (örneğin, CRC hesaplamaları, çok sayıda kitaplık, vb.) "Taşma sarması" programcılar için daha uygundur.


10
@DmitryGrigoryev unsigned intakla gelmemelidir, çünkü taşma kontrolü olan bir dil, tüm tamsayı tiplerini varsayılan olarak kontrol etmelidir . Yazmalısın wrapping unsigned int.
user253751

32
Maliyet tartışmasını satın almıyorum. CPU, HER SINGLE tamsayı hesaplamasındaki taşmayı kontrol eder ve ALU'da taşıma bayrağını ayarlar. Eksik olan programlama dili desteği. Basit bir didOverflow()satır içi işlevi veya __carrytaşıma bayrağına erişime izin veren global bir değişken bile kullanmazsanız sıfır CPU zamanına mal olur.
slebetman

37
@slebetman: Bu x86. ARM yapmaz. Örneğin ADDtaşımayı ayarlamıyor (ihtiyacınız ADDS). Itanium'un bir taşıma bayrağı bile yok . Ve x86'da bile, AVX'in taşıma bayrakları yoktur.
MSalters

30
@slebetman Taşıma bayrağını belirler, evet (x86’da, aklınızda olsun). Ama sonra taşıma bayrağını okumak ve sonuca karar vermek zorundasın - bu pahalı kısım. Aritmetik işlemler sıklıkla döngülerde kullanıldığından (ve buradaki sıkı döngülerden), bu, yalnızca bir ilave eğitime ihtiyacınız olsa bile performans üzerinde çok büyük bir etkiye sahip olabilecek birçok güvenli derleyici optimizasyonunu kolayca önleyebilir (ve bundan daha fazlasına ihtiyacınız varsa) ). Varsayılan olması gerektiği anlamına mı geliyor? Belki, özellikle de C # gibi bir dilde, söylemenin uncheckedyeterince kolay olduğu; ancak taşma işleminin ne sıklıkta olduğunu fazla tahmin ediyor olabilirsiniz.
Luaan

12
ARM's adds, aynı fiyatta add(taşıma bayrağının güncellenip güncellenmeyeceğini seçen yalnızca 1 bitlik bir bayrak talimatıdır). MIPS en addtaşma üzerindeki talimat tuzakları - Mecbur sormak kullanarak taşma değil tuzak adduyerine!
user253751

65

Kötü bir tradeoff olduğunu kim söylüyor?

Tüm üretim uygulamalarımı taşma kontrolü etkin olarak çalıştırıyorum. Bu bir C # derleyici seçeneğidir. Aslında bunu kıyaslamıştım ve farkı belirleyemedim. Veritabanına (oyuncak olmayan) HTML oluşturmak için erişmenin maliyeti, taşma kontrol maliyetlerini aşmaktadır.

Üretimde hiçbir işlem taşması olmadığını bildiğim için teşekkür ederim . Hemen hemen tüm kodlar taşma varlığında hatalı davranır. Böcekler iyi huylu olmazdı. Veri bozulması muhtemeldir, güvenlik olasılığı vardır.

Performansa ihtiyacım olursa, ki bu bazen böyle olur, unchecked {}ayrıntılı bir şekilde kullanarak taşma kontrolünü devre dışı bırakırım . Taşmayan bir işleme güvendiğimi söylemek checked {}istediğimde, bu gerçeği belgelemek için fazladan kod ekleyebilirim . Taşma konusunda dikkatliyim, ancak kontrol için mutlaka olmama gerek yok.

Onlar seçti zaman C # takımı yanlış bir seçim yapılmış inanmak değil , varsayılan olarak taşma kontrol ama bu seçim artık güçlü uyumluluk endişeleri nedeniyle kapatılır. Bu seçimin 2000 yılı civarında yapıldığını unutmayın. Donanım daha az yetenekliydi ve .NET'in henüz çok fazla çekişi yoktu. Belki .NET, Java ve C / C ++ programcılarına bu şekilde itiraz etmek istedi. .NET aynı zamanda metale yakın olabilmek içindir. Bu yüzden güvensiz kodlara, yapılara ve hepsi Java'nın sahip olmadığı mükemmel yerel çağrı yeteneklerine sahiptir.

Donanımımız ne kadar hızlı olursa, daha akıllı derleyiciler varsayılan olarak daha çekici taşma kontrolü elde eder.

Ayrıca taşma kontrolünün genellikle sonsuz büyüklükteki sayılardan daha iyi olduğuna inanıyorum. Sınırsız büyüklükteki rakamlar daha da yüksek, optimize edilmesi zor (inanıyorum) bir performans maliyetine sahip ve sınırsız kaynak tüketimi olasılığını başlatıyorlar.

JavaScript'in taşma ile başa çıkma şekli daha da kötüdür. JavaScript numaraları kayan nokta iki katına çıkar. Bir "taşma", tam olarak tam sayı kümesini bırakmak olarak kendini gösterir. Hafifçe yanlış sonuçlar ortaya çıkacaktır (örneğin, biri tarafından kapalı olma - bu, sonlu döngüleri sonsuz sonuçlara dönüştürebilir).

C / C ++ gibi bazı diller için, varsayılan olarak taşma kontrolü kesinlikle uygun değildir, çünkü bu dillerde yazılmış uygulamaların çıplak metal performansına ihtiyacı vardır. Yine de, izin vererek daha güvenli bir dile C / C ++ yapmak için çabalar var seçmesi daha güvenli moduna. Kodun% 90-99'u soğuk olma eğiliminde olduğundan bu övgüye değer. Bir örnek fwrapv2'nin tamamlayıcı paketlemesini zorlayan derleyici seçeneğidir. Bu, derleyici tarafından değil, dilin "uygulama kalitesi" özelliğidir.

Haskell'in mantıksal arama yığını yok ve belirtilen değerlendirme sırası yok. Bu, öngörülemeyen noktalarda istisnalar meydana getirir. Gelen a + bolsun belirtilmemiş ise aveya bilk değerlendirilir ve bu ifadelerin hepsi ya da hiç sonlandırmak ister. Bu nedenle, Haskell'in çoğu zaman sınırsız tamsayı kullanması mantıklıdır. Bu seçenek tamamen işlevsel bir dil için uygundur, çünkü istisnalar çoğu Haskell kodunda gerçekten uygun değildir. Ve sıfıra bölmek gerçekten de Haskells dil tasarımında sorunlu bir nokta. Sınırlandırılmamış tamsayılar yerine, sabit genişlikte sarma tamsayıları da kullanabilirlerdi ancak bu, dilin sunduğu “doğruluk odağı” temasına uymuyordu.

Taşma istisnalarına bir alternatif, tanımlanmamış işlemler tarafından oluşturulan ve işlemler arasında yayılan zehir değerleridir (değişken NaNdeğer gibi ). Bu, taşma kontrolünden çok daha pahalı gibi görünmekte ve tüm işlemleri sadece yavaşlayabilenlere değil daha da yavaşlatmaktadır (genellikle yüzer ve genel olarak sahip olan donanım ivmesini engellemekle sık sık olmaz - Itanium "Bir Şey Değil" olan NaT'ye sahiptir ). Ayrıca programın kötü verilerle birlikte aksamaya devam etmesinin bir noktasını da göremiyorum. Gibi ON ERROR RESUME NEXT. Hataları gizler ancak doğru sonuçların alınmasına yardımcı olmaz. supercat, bazen bunu yapmanın bir performans optimizasyonu olduğuna işaret ediyor.


2
Mükemmel cevap Peki neden bu şekilde yapmaya karar verdiklerine dair teoriniz nedir? Sadece C'yi ve nihayetinde montajı ve ikiliyi kopyalayan herkesi kopyalamak mı?
jpmc26

19
Kullanıcı tabanınızın% 99'u bir davranış beklediğinde, bunu onlara verme eğilimindesiniz. Ve "C'yi kopyalamak" gelince, aslında C'nin bir kopyası değil, bir uzantısı. C, unsignedsadece tamsayılar için istisnasız davranışı garanti eder . İmzalı tamsayı taşmasının davranışı aslında C ve C ++ 'da tanımsız davranıştır. Evet, tanımsız davranış . Öyle oluyor ki neredeyse herkes onu 2'nin tamamlayıcı taşması olarak uygular. C # aslında C / C ++ gibi UB'den ayrılmak yerine resmi kılar
Cort Ammon

10
@CortAmmon: Dennis Ritchie'nin tasarladığı dil, imzalı tamsayılar için sarmalama davranışını tanımlamıştı, ancak ikisinin tamamlayıcı olmayan platformlarında kullanım için gerçekten uygun değildi. Kesin ikinin tamamlayıcı sarmalamasından belirli sapmalara izin vermek bazı optimizasyonlara büyük ölçüde yardımcı olabilir (örneğin, derleyicinin x * y / y ile x'in yerini almasına izin vermek bir çarpma ve bölme tasarrufu sağlayabilir), derleyici yazarları Undefined Behavior'ı bir fırsat olarak değerlendirmediler. Belirli bir hedef platform ve uygulama alanı için anlamlı olan şey değil, pencereden anlam çıkarmak için bir fırsat olarak.
supercat,

3
@CortAmmon - tarafından üretilen kodu kontrol gcc -O2için x + 1 > x(burada xbir bir int). Ayrıca bkz. Gcc.gnu.org/onlinedocs/gcc-6.3.0/gcc/… . C'deki imzalı taşma üzerindeki 2s-complement davranışı, gerçek derleyicilerde bile isteğe bağlıdır ve gccnormal optimizasyon seviyelerinde yoksayma varsayılanıdır.
Jonathan Cast

2
@ supercat Evet, çoğu C derleyicisi yazarları, bazı gerçekçi olmayan ölçütlerin, programcılara makul bir anlambilim sağlamaya çalışmaktan% 0.5 daha hızlı çalışmasını sağlamakla daha fazla ilgileniyor (evet neden çözmenin kolay bir sorun olmadığını ve neden olabilecek bazı makul optimizasyonlar olduğunu anlıyorum.) birleştirildiğinde beklenmedik sonuçlar, yada, yada ama yine de odak değil ve konuşmaları takip ederseniz farkedersiniz). Neyse ki daha iyisini yapmaya çalışan bazı insanlar var .
Voo

30

Bunu yapmak için kötü bir değiş tokuş Çünkü bütün otomatik bir taşma nadir durumda yakalamak için hesaplamalar çok daha pahalı yapar meydana gelir. Programcının, bunun bir sorun olduğu nadir durumları kabul etmesi ile yüklenmesi ve tüm programcıların kullanmadıkları işlevsellik için bedel ödemesinden daha fazla özel engeller eklemeleri çok daha iyidir .


28
Bu bir şekilde Tampon Taşması kontrollerinin çıkarılmaması gerektiğini söylemek gibi bir şey çünkü neredeyse hiç olmadı ...
Bernhard Hiller

73
@BernhardHiller: ve C ve C ++ 'ın yaptığı tam olarak budur.
Michael Borgwardt

12
@DavidBrown: Aritmetik taşmaları gibi. Eski olsa da VM'den ödün vermez.
Deduplicator

35
@Deduplicator mükemmel bir noktaya değindi. CLR, doğrulanabilir programların kötü şeyler olduğunda bile çalışma zamanının değişmeyenlerini ihlal edemeyeceği şekilde dikkatle tasarlanmıştır . Güvenli programlar elbette kötü şeyler olduğunda kendi değişmezlerini ihlal edebilir .
Eric Lippert

7
@svick Aritmetik işlemler muhtemelen dizin indeksleme işlemlerinden çok daha yaygındır. Ve çoğu tam sayı büyüklüğü, taşan aritmetik işlemlerin gerçekleştirilmesinin çok nadir olması için yeterince büyüktür. Bu yüzden maliyet-fayda oranları çok farklı.
Barmar

20

Böyle tehlikeli bir davranış arkasındaki tasarım kararları nelerdir?

"Kullanıcıları ihtiyaç duymadıkları bir özellik için performans cezası vermeye zorlamayın."

C ve C ++ tasarımındaki en temel prensiplerden biridir ve günümüzde önemsiz sayılan işler için zorlukla yeterli performans elde etmek için gülünç sıkışıklıklar yaşamanız gereken farklı zamanlardan kaynaklanmaktadır.

Yeni diller, dizi sınırları denetimi gibi diğer birçok özellik için bu tutumu kırmaktadır. Taşma kontrolü için neden yapmadıklarından emin değilim; Bu sadece bir gözetim olabilir.


18
Kesinlikle C # tasarımında bir gözetim değil. C # tasarımcıları kasıtlı olarak iki mod yarattılar: checkedve uncheckedyerel olarak bunlar arasında geçiş yapmak için sözdizimi ve ayrıca genel olarak değiştirmek için komut satırı anahtarları (ve VS'deki proje ayarları) eklendi. uncheckedVarsayılanı yapma konusunda hemfikir olabilirsiniz (Ben yaparım), ama bütün bunlar açıkça çok kasıtlı.
salı

8
@slebetman - sadece kayıt için: Buradaki maliyet, taşma olup olmadığını kontrol etmenin maliyeti değil (önemsiz olan), ancak taşma olup olmamasına bağlı olarak farklı kod çalıştırmanın maliyetidir (çok pahalı). CPU'lar koşullu şube bildirimlerini beğenmez.
Jonathan Cast

5
@jcast Modern işlemcilerdeki şube tahminleri bu koşullu şube beyanı cezasını neredeyse tamamen ortadan kaldırmaz mıydı? Sonuçta, normal durum taşma olmamalıdır, bu yüzden bu çok öngörülebilir dallanma davranışıdır.
CodeMonkey

4
@CodeMonkey ile aynı fikirde. Bir derleyici, normalde yüklü / soğuk olmayan bir sayfaya taşma durumunda koşullu bir sıçrama yapar. Bunun için varsayılan tahmin "alınmadı" ve muhtemelen değişmeyecek. Toplam ek yük, boru hattındaki bir talimattır. Ancak bu, aritmetik komut başına verilen bir talimattır.
MSalters

2
@ MSalters evet, ek bir ek talimat var. Yalnızca CPU'ya bağlı sorunlarınız varsa bu etki büyük olabilir. IO ve CPU ağır kod karışımı olan çoğu uygulamada etkinin minimum olduğunu varsayarım. Rust yöntemini seviyorum, ek yükü yalnızca Hata Ayıklama yapılarına eklemeyi, ancak Serbest Bırakma yapılarına kaldırmayı seviyorum.
CodeMonkey

20

miras

Bu sorunun muhtemelen eskiden kaynaklandığını söyleyebilirim. C’de:

  • İmzalı taşma tanımsız davranış (derleyicilerin sarılmasını sağlamak için destek bayraklarını destekler),
  • işaretsiz taşma tanımlanmış davranış (sarar).

Bu, programcının ne yaptığını bildiği ilkesini izleyerek mümkün olan en iyi performansı elde etmek için yapıldı .

Statu-Quo'ya Giden Yol

C'nin (ve C ++ uzantısına göre) sırayla taşma algılamasını gerektirmemesi, taşma kontrolünün durgun olduğu anlamına gelir.

Donanım çoğunlukla C / C ++ 'a hitap eder (ciddi olarak, x86'nın bir strcmptalimatı vardır (aka SSE 4.2'den beri PCMPISTRI )!) Ve C umursamadığından, yaygın CPU'lar taşma tespit etmede etkili yollar sunmaz. X86'da, her taşma işleminden sonra çekirdek başına bir bayrağını kontrol etmeniz gerekir; gerçekte istediğin şey, sonuçta "lekeli" bir bayrak olduğunda (NaN'in çoğaldığı gibi). Ve vektör işlemleri daha problemli olabilir. Bazı yeni oyuncular piyasada verimli taşma işlemleriyle görünebilir; ama şimdilik x86 ve ARM umursamıyor.

Derleyici iyileştiricileri, taşma kontrollerini optimize etmede veya hatta taşma varlığında optimize etmede iyi değildir. John Regher gibi bazı akademisyenler bu statüden şikayetçidir , ancak gerçek şu ki, "başarısızlıklar" yapmanın basit gerçeği, montaj işlemine başlamadan bile optimizasyonları engelliyor olabilir. Özellikle otomatik vektörleşmeyi önlediğinde ...

Basamaklı efektlerle

Bu nedenle, verimli optimizasyon stratejileri ve verimli CPU desteği yoksa, taşma kontrolü maliyetlidir. Kaydırmaktan çok daha pahalı.

x + y - 1Olmadığında taşma gibi bazı rahatsız edici davranışlar ekleyin, bu x - 1 + ykullanıcılar yasal olarak rahatsız edebilir ve taşma kontrolü genellikle sarma lehine atılır (bu örneği ele alır).

Yine de bütün umutlar kaybolmaz

Clang ve gcc derleyicilerinde "dezenfektanlar" uygulamak için gayret gösterildi: Tanımsız Davranış vakalarını saptamak için enstrüman ikili araçlarını kullanma yolları. Kullanırken -fsanitize=undefined, işaretli taşma algılanır ve programı iptal eder; test sırasında çok faydalıdır.

Pas programlama dili etkin taşma denetimine sahip varsayılan olarak hata ayıklama modunda (bu performans nedenleriyle yayın modunda aritmetik sarma kullanır).

Bu nedenle, taşma kontrolü ve sahte sonuçların tehlikelerinin tespit edilememesi konusunda artan endişeler var ve umarım bu, araştırma topluluğuna, derleyici topluluğuna ve donanım topluluğuna olan ilgiyi artıracaktır.


6
@DmitryGrigoryev, taşmaları kontrol etmenin etkili bir yolunun zıttıdır, örneğin Haswell'de, döngü başına 4 normal ilavelerden sadece 1 kontrol edilen ilaveye kadar olan verimi düşürür ve bu durum, branş yanlışlıklarının jove Kirliliğin daha fazla küresel etkisi, şube belirleyici durumuna ve artan kod boyutuna ekledi. Bu bayrak yapışkan olsaydı, gerçek bir potansiyel sunacaktı ... ve sonra hala vektörel kodda doğru şekilde yapamazsın.

3
Bağlantı oluşturduğunuz bu yana bir blog yazısı John Regehr tarafından yazılmış, ben de bağlamak uygun olacağını düşündük yazısında başka birkaç ay Bağlı birinden önce yazılı. Bu makaleler farklı felsefelerden bahseder: Önceki makalede, tamsayılar sabit boyuttadır; tamsayı aritmetiği kontrol edilir (yani kod yürütülmesine devam edemez); Bir istisna ya da bir tuzak var. Yeni makale, sabit büyüklükte tamsayıların tamamen kesilmesi hakkında konuşuyor ve bu da taşmaları önlüyor.
rwong

2
@wwong Sonsuz boyutlu tam sayıların da kendi sorunları vardır. Taşma işleminiz bir hatanın sonucuysa (ki bu genellikle), her şey korkunç derecede başarısız olana kadar tüm sunucu kaynaklarını tüketen uzun süreli bir ıstıraba dönüşebilir. Ben çoğunlukla "erken başarısız" yaklaşımının hayranıyım - tüm çevreyi daha az zehirleme şansı. Bunun 1..100yerine Pascal-ish türlerini tercih ederdim - 2 ^ 31 vb. Yerine "zorlamak" yerine beklenen aralıklar konusunda açık olun. derleme zamanı, hatta).
Luaan

1
@Luaan: Asıl ilginç olan, genellikle ara hesapların geçici olarak taşabileceği, ancak sonuçta gerçekleşmeyeceğidir. Örneğin, 1.0000 aralığında, sonuç uygun olsa bile 51 x * 2 - 2olduğunda taşma yapabilir ve bu sayede xişlemlerinizi yeniden düzenlemeye zorlayabilirsiniz (bazen doğal olmayan bir şekilde). Tecrübelerime göre, hesaplamayı genellikle daha geniş bir tipte çalıştırmayı tercih ettiğimi ve sonucun uyup uymadığını kontrol ettiğimi öğrendim.
Matthieu M.

1
@MatthieuM. Evet, "yeterince akıllı derleyici" bölgesine giriyorsunuz. İdeal olarak, 103 değeri 1..100 türü için, hiçbir zaman gerçek 1..100'ün beklendiği bir bağlamda kullanılmadığı sürece geçerli olmalıdır (örneğin , atamanın geçerli bir 1 ile sonuçlandığı yerlerde x = x * 2 - 2çalışmalıdır x. 0,100 numara). Diğer bir deyişle, sayısal türdeki işlemler, atama uygun olduğu sürece türün kendisinden daha yüksek bir hassasiyete sahip olabilir. Bu, (a + b) / 2görmezden gelinme (işaretsiz) taşma durumunun doğru seçenek olabileceği durumlarda oldukça yararlı olacaktır .
Luaan

10

Taşmaları algılamaya çalışan diller, tarihsel olarak ilgili semantiği, başka türlü yararlı optimizasyon olabilecekleri ciddi şekilde kısıtlayan yollarla tanımlamıştır. Diğer şeylerin yanı sıra, hesaplamaları kodda belirtilenden farklı bir sırayla yapmak çoğu zaman yararlı olacaktır, ancak taşma tuzakları çoğu dilde verilen kodda belirtilenleri garanti eder:

for (int i=0; i<100; i++)
{
  Operation1();
  x+=i;
  Operation2();
}

x'in başlangıç ​​değeri, döngü boyunca 47. geçişte bir taşma meydana gelirse, İşlem1 47 defa yürütülür ve İşlem2 46 gerçekleştirilir. Operation1 veya Operation2 tarafından verilen bir istisnayı takiben x değerini kullanacak, kod aşağıdakilerle değiştirilebilir:

x+=4950;
for (int i=0; i<100; i++)
{
  Operation1();
  Operation2();
}

Ne yazık ki, döngü içinde taşma meydana geldiği durumlarda doğru semantiği garanti ederken bu gibi optimizasyonları yapmak zordur - esasen şöyle bir şey gerektirir:

if (x < INT_MAX-4950)
{
  x+=4950;
  for (int i=0; i<100; i++)
  {
    Operation1();
    Operation2();
  }
}
else
{
  for (int i=0; i<100; i++)
  {
    Operation1();
    x+=i;
    Operation2();
  }
}

Bir çok gerçek dünya kodunun daha ilgili döngüler kullandığı düşünülürse, taşma anlambilimini korurken kodu optimize etmenin zor olduğu açıktır. Ayrıca, önbelleğe alma sorunları nedeniyle, kod boyutundaki artışın, genel olarak yürütülen yolda daha az işlem olsa bile, genel programı daha yavaş çalışmasına olanak vermesi tamamen mümkündür.

Taşma algılamasını ucuz hale getirmek için ihtiyaç duyulacak olan, sonuçların (*), yükü etkilemeden taşma olmadan bir hesaplamanın yapılıp yapılmadığını rapor etmeyi kolaylaştıracak tanımlanmış bir gevşek taşma saptama semantiği kümesi olacaktır. bunun ötesinde ayrıntılarla derleyici. Bir dil belirtimi taşma tespitinin maliyetini yukarıdakileri elde etmek için gereken minimum düzeye indirmeye odaklanmışsa, mevcut dillerden olduğundan daha az maliyetli olabilir. Bununla birlikte, verimli taşma tespitini kolaylaştırmaya yönelik herhangi bir çabanın farkında değilim.

(*) Eğer bir dil tüm taşmaların bildirileceğini vaat ederse, o zaman böyle bir ifade taşma garantisi verilmediği sürece x*y/ybasitleştirilemez . Aynı şekilde, bir hesaplamanın sonucu göz ardı edilse bile, tüm taşmaları bildirmeyi vaat eden bir dilde, taşma kontrolünü yapabilmesi için yine de gerçekleştirmesi gerekecektir. Bu gibi durumlarda taşma aritmetik olarak yanlış davranışa yol açamadığından, bir taşma olasılığının yanlış sonuçlara neden olmadığından emin olmak için bir programın bu tür kontrolleri yapması gerekmez.xx*y

Bu arada, C'deki taşmalar özellikle kötüdür. Her ne kadar C99'ı destekleyen hemen hemen her donanım platformu ikinin tamamlayıcı sessiz sarma anlambilimini kullanıyor olsa da, modern derleyiciler için taşma durumunda rasgele yan etkilere neden olabilecek kodlar üretmek modadır. Örneğin, şöyle bir şey verilir:

#include <stdint.h>
uint32_t test(uint16_t x, uint16_t y) { return x*y & 65535u; }
uint32_t test2(uint16_t q, int *p)
{
  uint32_t total=0;
  q|=32768;
  for (int i = 32768; i<=q; i++)
  {
    total+=test(i,65535);
    *p+=1;
  }
  return total;
}

GCC, test2 için koşulsuz olarak bir kez (* p) artış yapan ve q içine verilen değerden bağımsız olarak 32768 döndüren kod üretecektir. Akıl yürütmesiyle, (32769 * 65535) & 65535u hesaplaması taşmaya neden olur ve bu nedenle derleyicinin (q | 32768) 32768'den daha büyük bir değer vereceği durumları dikkate almasına gerek yoktur. (32769 * 65535) ve 65535u hesaplamasının sonucun üst bitlerine dikkat etmesinin gerekmesi nedeniyle, gcc döngüyü yok saymak için gerekçe olarak işaretli taşma kullanacaktır.


2
"modern derleyiciler için modadır ..." - aynı şekilde, bazı tanınmış çekirdeklerin geliştiricilerinin kullandıkları optimizasyon bayraklarıyla ilgili belgeleri okumamayı seçmeleri ve internet üzerinden öfkeli davranmaları kısaca modaydı. çünkü istedikleri davranışı elde etmek için daha fazla derleyici bayrağı eklemek zorunda kaldılar ;-). Bu durumda, -fwrapvsorgulayıcının istediği davranış olmasa da tanımlanmış davranışla sonuçlanır. Verilen, gcc optimizasyonu her türlü C gelişimini standart ve derleyici davranışı üzerinde kapsamlı bir incelemeye dönüştürür.
Steve Jessop

1
@SteveJessop: Eğer derleyici yazarları, "tanımsız davranış", "temel platform üzerinde ne mantıklı olursa olsun" anlamına gelen düşük seviyeli bir lehçeyi tanıdıysa ve daha sonra programcıların gereksiz garantilerden feragat etmelerinin yollarını ekledilerse, C çok daha sağlıklı bir dil olurdu. Standartta "taşınabilir olmayan veya hatalı" ifadesinin basitçe "hatalı" anlamına geldiğini varsaymak yerine. Çoğu durumda, zayıf davranış garantileri olan bir dilde elde edilebilecek en uygun kod, daha güçlü garantilerle veya hiçbir garanti olmadan elde edilebileceğinden çok daha iyi olacaktır. Örneğin ...
supercat

1
... eğer bir programcı x+y > zasla verim 0 veya verim 1'den başka bir şey yapmayacak şekilde değerlendirmek isterse, ancak taşma durumunda sonuçlardan biri eşit olarak kabul edilebilirse, bu garantiyi sunan bir derleyici sık sık kod için daha iyi kod üretebilir. ifadesi x+y > zherhangi bir derleyici daha ifade defans yazılı versiyonu için üretmek mümkün olacaktır. Gerçekçi konuşmak gerekirse, bölme / kalanlar dışındaki tamsayı hesaplamalarının hiçbir yan etkisi olmadan yapılacağının garantisi ile , yararlı taşma ile ilişkili optimizasyonların hangi kesri önlenecek?
supercat

Tamamen ayrıntılara girmediğimi itiraf ediyorum, ancak kininizin genel olarak "derleyici yazarlar" ve özellikle "gcc’de yamamı kabul etmeyen biri" olmadığının -fwhatever-makes-sensegerçeği, bana daha çok şey olduğunu gösteriyor. onların tarafında hevesten daha. Duyduğum genel argümanlar, kod satır içi (ve hatta makro genişlemesinin), bir kod yapısının özel kullanımı hakkında mümkün olduğu kadar kesinti yapmaktan fayda sağlamasıdır; çevreleyen kod "imkansız" olduğunu.
Steve Jessop

Bu nedenle, basitleştirilmiş bir örnek için, yazarsam foo(i + INT_MAX + 1), derleyici-yazarlar, foo()negatif olma argümanının doğruluğuna dayanan doğrulanmış çizgiye dayanan optimizasyonlar yapmaya isteklidirler (belki de fendish divmod hileleri). Ek kısıtlamalarınız altında, yalnızca negatif girdiler için davranışları platform için anlamlı olan optimizasyonları uygulayabilirler. Tabii ki, kişisel olarak bunun gibi şeyleri başlatan bir -fseçenek olduğu için mutlu olurum -fwrapvve muhtemelen bayrağı olmayan bazı optimizasyonları devre dışı bırakmak zorunda kalırsınız. Ama bu işleri kendi başıma yapmak için canımı sıkmak gibi değil.
Steve Jessop

9

Tüm programlama dilleri tamsayı taşmalarını dikkate almaz. Bazı diller, tüm sayılar (çoğu Lisp lehçeleri, Ruby, Smalltalk, ...) ve diğer kütüphaneler için güvenli tamsayı işlemleri sağlar; örneğin, C ++ için çeşitli BigInt sınıfları vardır.

Bir dilin varsayılan olarak tamsayıyı taşmasından güvenli yapıp yapmamasının amacı, amacına bağlıdır: C ve C ++ gibi sistem dillerinin sıfır maliyet soyutlaması sağlaması gerekir ve "büyük tamsayı" bir değildir. Ruby gibi üretkenlik dilleri kutudan büyük tam sayılar sağlayabilir ve yapabilir. Aralarında bir yerde olan Java ve C # gibi diller, IMHO'nun kutudan çıkardığı güvenli tamsayılarla gitmesi gerekir.


Taşma algılaması (ve daha sonra bir sinyal, panik, istisna, ...) ve büyük rakamlara geçmek arasında bir fark olduğunu unutmayın. İlki, ikincisinden daha ucuza yapılabilmelidir.
Matthieu M.

@MatthieuM. Kesinlikle - ve cevabımda bunun net olmadığını anladım.
Nemanja Trifunovic

7

Gösterdiğiniz gibi, varsayılan olarak etkinleştirilmiş taşma kontrolleri varsa C # 3 kat daha yavaş olacaktır (örneğinizin bu dil için tipik bir uygulama olduğu varsayılarak). Performansın her zaman en önemli özellik olmadığına katılıyorum, ancak diller / derleyiciler tipik olarak tipik görevlerdeki performanslarıyla karşılaştırılıyor. Bu kısmen, dil özelliklerinin kalitesinin bir dereceye kadar öznel olmasından ve performans testinin objektif olmasından kaynaklanmaktadır.

Çoğu durumda C # 'ya benzeyen ancak 3 kat daha yavaş yeni bir dil tanıtırsanız, son kullanıcılarınızın çoğu taşma kontrollerinden daha fazla faydalansalar bile, pazar payı almak kolay olmazdı. yüksek performanstan.


10
Bu özellikle, Java ve C ++ ile karşılaştırıldığında ilk günlerinde, ölçülmesi zor olan geliştirici üretkenlik ölçümleri veya güvenlik ihlali ile uğraşmamayacağı kurtarılan para ölçümleri konusundaki C # durumuydu. hangi ölçmek zor, ama önemsiz performans kriterleri.
Eric Lippert

1
Muhtemelen, CPU'nun performansı bazı basit sayılardaki çırpma ile kontrol edilir. Bu nedenle taşma saptaması için optimizasyonlar bu testlerde "kötü" sonuçlar verebilir. 22'yi yakala.
Bernhard Hiller

5

Performansa dayalı taşma kontrol eksikliğini haklı çıkaran birçok cevabın ötesinde, dikkate alınması gereken iki farklı aritmetik türü vardır:

  1. indeksleme hesaplamaları (dizi indeksleme ve / veya işaretçi aritmetiği)

  2. diğer aritmetik

Dil, işaretçi boyutuyla aynı olan bir tamsayı boyutu kullanıyorsa, iyi oluşturulmuş bir program, dizin oluşturma hesaplamalarını taşması nedeniyle taşmayacaktır, çünkü dizin oluşturma hesaplamalarının taşmasına neden olması için mutlaka bellek tükenmesi gerekir.

Bu nedenle, ayırıcı veri yapılarını içeren işaretçi aritmetik ve indeksleme ifadeleriyle çalışırken bellek ayırmalarını kontrol etmek yeterlidir. Örneğin, 32 bitlik bir adres alanınız varsa ve 32 bitlik tamsayılar kullanıyorsanız ve maksimum 2GB'lık bir yığın tahsis edilmesine izin veriyorsa (adres alanının yaklaşık yarısı), indeksleme / işaretçi hesaplamaları (temel olarak) taşmaz.

Ayrıca, toplama / çıkarma / çarpmanın ne kadarının dizi indeksleme veya işaretçi hesaplaması içerdiğini ve böylece ilk kategoriye girdiğini görünce şaşırabilirsiniz. Nesne işaretçisi, sahaya erişim ve dizi manipülasyonları indeksleme işlemleridir ve birçok program bunlardan daha fazla aritmetik işlem yapmaz! Temel olarak, bu, programların tamsayı taşması kontrolü olmadan yaptıkları gibi çalışmasının birincil nedenidir.

Tüm indeksleme yapmayan ve imleme yapmayan hesaplamalar, ya taşma isteyenler / beklemekte olanlar (örneğin karma hesaplamalar) ve istemeyenler (örneğin, özetleme örneğiniz) olarak sınıflandırılmalıdır.

İkinci durumda, programcılar sıklıkla doubleveya bazıları gibi alternatif veri türlerini kullanır BigInt. Çoğu hesaplama , örneğin finansal hesaplamalar decimalyerine bir veri türü gerektirir double. Tamsayı tipleri ile yapışmazlar ve yapışmazlarsa, tamsayı taşmalarını kontrol etmeye özen göstermeleri gerekir - veya başka bir deyişle, evet, program belirttiğiniz gibi tespit edilemeyen bir hata durumuna ulaşabilir.

Programcılar olarak, sayısal veri tiplerindeki seçimlerimize ve bunların taşma olasılıkları bakımından sonuçlarına duyarlılıktan duyarlı olmamız gerekir. Genel olarak (ve özellikle hızlı tam sayı türlerini kullanma arzusu olan C dilleri ailesiyle çalışırken), indeksleme hesaplamaları ile diğerleri arasındaki farkların farkında olmamız gerekir.


3

Rust dili , hata ayıklama yapısı için çekler ekleyerek ve optimize edilmiş sürüm sürümünde bunları kaldırarak taşma olup olmadığını denetleme arasında ilginç bir uzlaşma sağlar. Bu, son sürümde hala tam performans elde ederken test sırasında hataları bulmanızı sağlar.

Taşma sargısı bazen istenen davranış olduğu için , işleçlerin taşma için denetlemediği sürümleri de vardır .

Değişiklik için RFC'deki seçimin arkasındaki neden hakkında daha fazla bilgi edinebilirsiniz . Bu blog yazısında , bu özelliğin yakalanmasına yardımcı olduğu hataların bir listesi de dahil olmak üzere birçok ilginç bilgi bulunmaktadır .


2
Rust ayrıca, checked_multaşma olup olmadığını kontrol eden ve Nonevarsa geri döndüren gibi yöntemler sunar Some. Bu, üretim yanı sıra hata ayıklama modunda da kullanılabilir: doc.rust-lang.org/std/primitive.i32.html#examples-15
Akavall

3

Swift'de, herhangi bir tamsayı taşması varsayılan olarak algılanır ve programı anında durdurur. Silme davranışına ihtiyaç duyduğunuz durumlarda, bunu başarabilen farklı operatörler & +, & - ve & * vardır. Bir işlem gerçekleştiren ve taşma olup olmadığını söyleyen işlevler de var.

Yeni başlayanların Collatz sırasını değerlendirmeye ve kod çökmesine neden olmaya çalışırken izlemek eğlenceli…

Şimdi Swift tasarımcıları da LLVM ve Clang tasarımcılarıdır, bu yüzden optimizasyon hakkında bir iki veya iki şey biliyorlar ve gereksiz taşma kontrollerinden kaçınılabilecek kapasiteye sahipler. Tüm optimizasyonlar etkinken, taşma kontrolü kod boyutuna ve yürütme süresine fazla bir şey katmaz. Ve taşmaların çoğu kesinlikle yanlış sonuçlara yol açtığından, kod boyutu ve iyi harcanan yürütme süresidir.

PS. C, C ++ 'da, Objective-C işaretli tamsayı aritmetik taşması tanımsız davranıştır. Bu, derleyicinin imzalı tamsayı taşması durumunda ne yaptığını, tanım gereği doğru olduğu anlamına gelir. İmzalı tamsayı taşmasıyla başa çıkmanın tipik yolları, CPU'nun size verdiği sonucu alarak, derleyiciye bu taşma olayının asla gerçekleşmeyeceği varsayımlarını oluşturmak (ve örneğin n + 1> n'nin her zaman doğru olduğu sonucuna varmaktır). asla olmadığı varsayılmıştır) ve nadiren kullanılan bir olasılık, Swift'in yaptığı gibi taşma olursa kontrol etmek ve çarpmaktır.


1
Bazen C’de UB’ye dayalı deliliği iten insanların gizlice onu başka bir dilin lehine baltalamaya çalışıp çalışmadığını merak ediyordum. Bu mantıklı olur.
supercat

Tedavisi x+1>xbir derleyici uygun olarak keyfi daha büyük türleri kullanarak tamsayı ifadeleri değerlendirmek için izin verildiği takdirde x hakkında herhangi bir "varsayım" make (veya o kadar yapıyor sanki davranmaya) için bir derleyici gerektirmez gibi koşulsuz gerçek. Taşma temelli “varsayımların” daha katı bir örneği, uint32_t mul(uint16_t x, uint16_t y) { return x*y & 65535u; }bir derleyicinin verilen derleyicinin 32768'den daha büyük olamayacağına sum += mul(65535, x)karar vermesine x[ karar vericilere karar verirken, karar vericilerin C89 Mantığını yazanları şok edebilecek davranışta bulunmalarına neden olacağına karar vermek) olacaktır. ..
supercat

... unsigned shortterfi signed intettirirken, ikisinin tamamlayıcı sessiz sarma uygulamalarının (yani, daha sonra kullanılan C uygulamalarının çoğunluğu), yukarıdaki gibi bir kodu, unsigned shortterfi ettirilmiş olsun intveya olmasın, aynı şekilde ele alması gerçekti unsigned. Standart , yukarıdaki gibi güvenli bir şekilde kodu ele almak için sessiz saran ikisinin tamamlayıcı donanımı üzerinde uygulamalar gerektirmedi , ancak Standardın yazarları da her şekilde yapmaları bekleniyormuş gibi görünüyor.
supercat

2

Aslında, bunun asıl nedeni tamamen teknik / tarihseldir: İşlemcinin çoğu için işareti yoksayması . Genellikle kayıtlara iki tamsayı eklemek için tek bir komut vardır ve CPU bu iki tamsayıyı imzalı veya imzasız olarak yorumlayıp yorumlamamanı umursamıyor. Aynısı çıkarma için ve hatta çarpma için de geçerlidir. İşaret farkında olması gereken tek aritmetik işlem bölünmedir.

Bunun işe yaramasının nedeni, 2'nin neredeyse tüm CPU'lar tarafından kullanılan işaretli tam sayıların tamamlayıcısı olarak gösterilmesidir. Örneğin, 4-bit 2'lerde 5 ve -3 eklenmesi buna benzer:

  0101   (5)
  1101   (-3)
(11010)  (carry)
  ----
  0010   (2)

Sarma ucunu atma davranışının doğru işaretli sonucu nasıl verdiğini gözlemleyin. Benzer şekilde, CPU'lar genellikle çıkarma işlemini x - yşu şekilde gerçekleştirir x + ~y + 1:

  0101   (5)
  1100   (~3, binary negation!)
(11011)  (carry, we carry in a 1 bit!)
  ----
  0010   (2)

Bu, donanımın eklenmesi olarak çıkarma işlemini gerçekleştirir, sadece aritmetik-mantıksal birime (ALU) girişleri önemsiz şekillerde düzenler. Daha basit ne olabilir?

Çarpma, bir toplama dizisinden başka bir şey olmadığından, benzer şekilde güzel davranır. 2'nin tamamlayıcı gösterimini kullanmanın ve aritmetik işlemlerin yapılmamasını ihmal etmenin sonucu basitleştirilmiş devre ve basitleştirilmiş komut kümeleridir.

Açıkçası, C metale yakın çalışmak üzere tasarlandığından, bu imzasız aritmetiğin standardize edilmiş davranışı ile aynı davranışı benimsemiş, sadece imzasız aritmetiğin tanımsız davranış vermesi için izin vermiştir. Ve bu seçim Java gibi diğer dillere ve tabii ki C # 'ya da taşındı.


Bu cevabı ben de vermeye geldim.
Bay Lister

Ne yazık ki, bazı insanlar, bir platforma düşük seviye C kodu yazan kişilerin, bu tür bir amaca uygun bir C derleyicisinin taşma durumunda kısıtlı bir şekilde davranacağını bekleyecekleri gerçeğine sahip olma fikrini oldukça makul buluyor gibi görünmektedir. Şahsen, hesaplamaları yüzden 32 bit sistemde derleyici'nın kolaylık (en keyfi-genişletilmiş hassas kullanılarak yapılmaktadır sanki eğer makul bir derleyici, davranmaya için düşünüyorum x==INT_MAX, sonra x+1keyfi olarak derleyici en az birini +2147483648 veya -2147483648 olarak davranabilir kolaylık), ama ...
supercat

bazı insanlar eğer düşünüyor xve yvardır uint16_tve 32 bit sistemde kod hesaplar x*y & 65535uzaman yzaman 65535, yani kod üstlenmesi gerektiğini bir derleyici ulaşılabilir asla x32768. büyüktür
SuperCat

1

Bazı cevaplar kontrolün maliyetini tartıştı ve bunun makul bir gerekçe olduğu konusunda tartışmak için cevabınızı düzenlediniz. Bu noktaları ele almaya çalışacağım.

C ve C ++ 'da (örnek olarak), dil tasarım ilkelerinden biri, istenmeyen bir işlevsellik sağlamak değildir. Bu genellikle "kullanmadığınız şey için ödeme yapmayın" ifadesiyle özetlenir. Programcının taşma kontrolü yapması isteniyorsa, bunu isteyebilir (ve cezayı ödeyebilir). Bu, dili kullanımı daha tehlikeli hale getirir, ancak bunu bilen bir dille çalışmayı tercih edersiniz, bu nedenle riski kabul edersiniz. Bu riski istemiyorsanız veya güvenliğin en üst düzeyde performans gösterdiği bir kod yazıyorsanız, performans / risk değişiminin farklı olduğu daha uygun bir dil seçebilirsiniz.

Ancak 10.000.000.000 tekrarla, çekle geçen zaman hala 1 nanosaniyeden az.

Bu akıl yürütmede yanlış olan birkaç şey var:

  1. Bu çevreye özgüdür. Bunun gibi belirli rakamları alıntılamak genellikle çok az anlamlıdır, çünkü kod, performanslarına göre büyüklük derecelerine göre değişen her türlü ortam için yazılmıştır. Masaüstü bir makinedeki 1 nanosaniyeniz, yerleşik bir çevreyi kodlayan birine inanılmaz derecede hızlı gelebilir ve süper bir bilgisayar kümesi için kodlayan birine inanılmaz derecede yavaş görünebilir.

  2. 1 nanosaniye, nadiren çalışan bir kod parçası için hiçbir şey gibi görünmeyebilir. Öte yandan, kodun ana işlevi olan bazı hesaplamaların iç döngüsünde ise, tıraş edebileceğiniz zamanın her bir kesimi büyük bir fark yaratabilir. Bir kümede bir simülasyon çalıştırıyorsanız, o zaman iç döngüde bir nanosaniyenin kaydedilen fraksiyonları doğrudan donanım ve elektrik için harcanan paraya çevrilebilir.

  3. Bazı algoritmalar ve bağlamlar için, 10.000.000.000 yineleme önemsiz olabilir. Yine, yalnızca belirli bağlamlarda uygulanan belirli senaryolar hakkında konuşmak genellikle mantıklı değildir.

Bunun önemli olduğu bir durum olabilir, ancak çoğu uygulama için bunun önemi yoktur.

Belki de haklısın. Fakat yine de, bu, belirli bir dilin hedeflerinin ne olduğu ile ilgilidir. Aslında birçok dil "çoğu" nun gereksinimlerini karşılamak veya diğer kaygılara karşı güvenliği artırmak için tasarlanmıştır. C ve C ++ gibi diğerleri verime öncelik veriyor. Bu bağlamda, çoğu insanın canını sıkmayacağından, herkesin performans cezası ödemesini sağlamak, dilin elde etmeye çalıştığı şeye aykırıdır.


-1

İyi cevaplar var, ama bence burada cevapsız bir nokta var: bir tamsayı taşmasının etkileri mutlaka kötü bir şey değildir ve bundan sonra bir varlık taşması sorunundan mı ikaynaklandığını bilmek güçtür. veya eğer kasıtlı olarak -1 ile çarpılarak yapıldıysa.MAX_INTMIN_INT

Örneğin, 0'dan büyük tüm temsil edilebilir tamsayıları bir arada eklemek istersem, sadece bir for(i=0;i>=0;++i){...}toplama döngüsü kullanırdım - ve taşması durumunda, bu, hedef davranışı olan (bir hata atmak, atlamak zorunda kalacağım anlamına gelir) ilaveyi durdurur. keyfi bir koruma çünkü standart aritmetik ile etkileşime giriyor). İlkel aritmetik kısıtlamak için kötü bir uygulama, çünkü:

  • Her şeyde kullanılırlar - ilkel matematikteki bir yavaşlama her işleyen programda bir yavaşlamadır
  • Bir programcının onlara ihtiyacı varsa, bunları daima ekleyebilir
  • Onlara sahipseniz ve programcı onlara ihtiyaç duymuyorsa (ancak daha uzun çalışma sürelerine ihtiyaç duyuyorsa), optimizasyon için onları kolayca kaldıramazlar
  • Onlara sahip ve programcı bunları ihtiyacı varsa değil (yukarıdaki örnekte olduğu gibi) olması, programcı her ikisi (veya ilgili olabilir veya olmayabilir) çalışma zamanı isabet alarak ve programcı hala çıkarmadan zaman harcaması gerekiyor veya “koruma” etrafında çalışmak.

3
Bir program için bir dil sağlamıyorsa, bir programcının verimli taşma kontrolü eklemesi pek mümkün değildir. Bir işlev yoksayılan bir değeri hesaplarsa, bir derleyici hesaplamayı optimize edebilir. Eğer bir fonksiyon taşma kontrolü yapılmış fakat başka bir şekilde göz ardı edilmiş bir değer hesaplarsa , bir taşma programın çıktısını etkilemese bile güvenli bir şekilde göz ardı edilebilse bile, bir derleyici taşması durumunda hesaplama ve bindirme yapmalıdır.
supercat,

1
Şunlar arasından gidemez INT_MAXiçin INT_MIN-1 ile çarpılarak.
David Conrad,

Çözüm, programcının belirli bir kod bloğunda veya derleme biriminde çekleri kapatması için bir yol sağladığı konusunda açıktır.
David Conrad,

for(i=0;i>=0;++i){...}ekibimde cesaret kırmaya çalıştığım kodun tarzı: özel efektlere / yan etkilere dayanıyor ve ne yapması gerektiğini açıkça ifade etmiyor. Ancak farklı bir programlama paradigması gösterdiği için cevabınızı hala takdir ediyorum.
Bernhard Hiller

1
@Delioth: i64 bitlik bir tür ise, tutarlı bir sessiz sarma ikisinin tamamlayıcı davranışına sahip bir uygulamada bile, saniyede bir milyar yineleme gerçekleştirilirse, böyle bir döngü yalnızca en fazla intdeğeri bulması için sağlanabilir . yüzlerce yıl. Tutarlı sessiz sarma davranışı söz vermeyen sistemlerde, bu tür davranışlar, ne kadar kod verilmiş olursa olsun garanti edilmez.
supercat,
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.