Neden imzasız tamsayı taşması tanımlı davranış, ancak imzalı tamsayı taşması tanımlanmadı?


209

İmzasız tamsayı taşması hem C hem de C ++ standartları tarafından iyi tanımlanmıştır. Örneğin, C99 standardı ( §6.2.5/9)

İmzasız işlenenleri içeren bir hesaplama asla üstesinden gelemez, çünkü elde edilen imzalanmamış tamsayı türü ile temsil edilemeyen bir sonuç, modüle edilen sonuç tarafından temsil edilebilen en büyük değerden daha büyük bir sayıdır.

Bununla birlikte, her iki standart da imzalı tamsayı taşmasının tanımsız davranış olduğunu belirtir. Yine, C99 standardından ( §3.4.3/1)

Tanımlanmamış davranışa örnek olarak, tamsayı aşırı akışındaki davranış gösterilebilir.

Bu tutarsızlığın tarihsel bir nedeni (veya daha iyisi!) Var mı?


50
Muhtemelen işaretli tam sayıları temsil etmenin birden fazla yolu olduğu için. Hangi yol standartta belirtilmez, en azından C ++ 'da belirtilmez.
juanchopanza


7
Juanchopanza'nın söyledikleri mantıklı. Anladığım kadarıyla, orijinal C standardı büyük ölçüde mevcut uygulamayı kodladı. O zamanki tüm uygulamalar imzasız "taşma" nın ne yapması gerektiğine karar verdiyse, bu standardizasyonu sağlamak için iyi bir neden. İmzalı taşmanın ne yapması gerektiği konusunda anlaşamadılar, bu yüzden standartlara ulaşmadı.

2
@DavidElliman Ekteki imzasız ambalaj kolayca da algılanabilir ( if (a + b < a)). Çarpmadaki taşma hem imzalı hem de imzasız tipler için zordur.

5
@DavidElliman: Bu sadece onu tespit edip edemeyeceğiniz değil, sonucun ne olduğu meselesidir. Bir işaret + değer uygulamasında, MAX_INT+1 == -0ikisinin tamamlayıcısı ikenINT_MIN
David Rodríguez - dribeas

Yanıtlar:


163

Tarihsel neden, çoğu C uygulamasının (derleyici) taşma davranışını kullandığı tamsayı gösterimiyle uygulanması en kolay olanı kullanmasıdır. C uygulamaları genellikle CPU tarafından kullanılanla aynı temsili kullanmıştır - bu nedenle taşma davranışı CPU tarafından kullanılan tamsayı gösteriminden sonra gelir.

Uygulamada, yalnızca uygulamaya göre farklılık gösterebilen işaretli değerlerin temsilidir: kişinin tamamlayıcısı, ikisinin tamamlayıcısı, işaret büyüklüğü. İmzasız bir tür için, standardın varyasyona izin vermesinin bir nedeni yoktur, çünkü yalnızca bir açık ikili gösterim vardır (standart yalnızca ikili gösterime izin verir).

İlgili alıntılar:

C99 6.2.6.1:3 :

İmzasız bit alanlarında saklanan değerler ve imzasız karakter tipi nesneler saf bir ikili gösterim kullanılarak temsil edilecektir.

C99 6.2.6.2:2 :

İşaret biti bir ise, değer aşağıdaki yollardan biriyle değiştirilmelidir:

- işaret biti 0 olan karşılık gelen değer kaldırılır ( işaret ve büyüklük );

- işaret bitinin değeri - - (2 N ) ( ikisinin tamamlayıcısı );

- işaret biti - (2 N - 1) ( tamamlayıcı ) değerine sahiptir .


Günümüzde, tüm işlemciler ikisinin tamamlayıcı temsilini kullanır, ancak imzalı aritmetik taşma tanımsız kalır ve derleyici üreticileri optimizasyona yardımcı olmak için bu tanımsızlığı kullandıkları için tanımsız kalmasını ister. Örneğin Ian Lance Taylor'ın bu blog gönderisine veya Agner Fog'un bu şikayetine ve hata raporunun yanıtlarına bakın.


6
Bununla birlikte, burada önemli olan not , modern dünyada 2'nin tamamlayıcı imzalı aritmetiğinden başka bir şey kullanan hiçbir mimarinin kalmamasıdır . Dil standartlarının hala bir PDP-1 üzerinde uygulamaya izin vermesi, saf bir tarihi eserdir.
Andy Ross

9
@AndyRoss ancak 2013'ten itibaren tamamlayıcı ve yeni sürümleriyle hala sistemler (OS + derleyicileri, kuşkusuz eski bir geçmişi var) var. Örnek: OS
2200.

3
@Andy Ross, "2'nin tamamlayıcısı dışında hiçbir şey kullanmadan ... hiçbir mimariyi ..." bugün DSP'lerin ve gömülü işlemcilerin gamını içerir mi?
chux - Monica'yı geri yükle

11
@AndyRoss: 2'li tamamlayıcıdan başka bir şey kullanan “hayır” mimarisi olsa da (“hayır” ın bazı tanımları için), kesinlikle işaretli tamsayılar için doyurucu aritmetik kullanan DSP mimarileri vardır.
Stephen Canon

10
İmzalı aritmetiği doyurmak kesinlikle standarda uygundur. Tabii ki sarma talimatları imzasız aritmetik için kullanılmalıdır, ancak derleyici her zaman imzasız veya imzalı aritmetiğin yapılıp yapılmadığını bilmek için bilgiye sahiptir, böylece talimatları uygun şekilde seçebilir.
caf

15

Pascal'ın iyi cevabının yanı sıra (ana motivasyon olduğuna eminim), bazı işlemcilerin işaretli tamsayı taşması üzerinde bir istisna oluşturması da mümkündür, bu da derleyicinin "başka bir davranış için düzenleme" yapması durumunda sorunlara neden olacaktır ( Örneğin, olası taşmayı kontrol etmek ve bu durumda farklı hesaplamak için ek talimatlar kullanın).

"Tanımlanmamış davranış" ın "işe yaramadığı" anlamına gelmediğini de belirtmek gerekir. Bu, uygulamanın bu durumda istediği şeyi yapmasına izin verildiği anlamına gelir. Buna "doğru olanı" yapmanın yanı sıra "polisi arama" veya "çarpma" da dahildir. Çoğu derleyici, tanımlanması nispeten kolay olduğu varsayılarak, mümkünse "doğru olanı yap" ı seçecektir (bu durumda). Ancak, hesaplamalarda taşmalar yaşıyorsanız, bunun gerçekte neyle sonuçlandığını ve derleyicinin beklediğinizden başka bir şey yapabileceğini anlamanız önemlidir (ve bunun derleyici sürümüne, optimizasyon ayarlarına vb. .


23
Derleyiciler, doğru şeyi yaparken onlara güvenmenizi istemez ve çoğu, int f(int x) { return x+1>x; }optimizasyonla derlediğinizde size gösterecektir . GCC ve ICC, varsayılan seçeneklerle yukarıdakileri optimize eder return 1;.
Pascal Cuoq

1
intOptimizasyon düzeylerine bağlı olarak taşma ile karşılaşıldığında farklı sonuçlar veren bir örnek program için bkz. İdeone.com/cki8nM Bu, cevabınızın kötü tavsiye verdiğini gösteriyor.
Magnus Hoff

Bu kısmı biraz değiştirdim.
Mats Petersson

Bir C, "imzalı ikinin tamamlayıcısını tamamlama" tamsayısını bildirmek için bir araç sağlayacaksa, C'yi çalıştırabilecek hiçbir platformun en azından orta derecede verimli bir şekilde desteklemesi konusunda çok fazla sorun yaşamamalıdır. Ek yük, sarma davranışı gerekli olmadığında kodun böyle bir tür kullanmaması için yeterli olacaktır, ancak ikinin tamamlayıcı tamsayılarındaki çoğu işlem, karşılaştırmalar ve promosyonlar hariç, imzasız bir tamsayı ile aynıdır.
supercat

1
Negatif değerlerin var olması ve derleyicinin düzgün çalışması için "çalışması" gerekir, elbette bir işlemci içindeki imzalı değerlerin yokluğu etrafında çalışmak ve hangisini tamamlayıcı veya ikişer tamamlayıcı olarak imzalanmamış değerleri kullanmak tamamen mümkündür. talimat setinin ne olduğuna bağlı olarak. Bunu yapmak, genellikle donanım desteğine sahip olmaktan çok daha yavaş olacaktır, ancak donanımda kayan noktayı veya benzerlerini desteklemeyen işlemcilerden farklı değildir - sadece çok fazla kod ekler.
Mats Petersson

10

Her şeyden önce, tüm örnekler ve dipnotlar gibi C11 3.4.3'ün normatif metin olmadığını ve bu nedenle alıntı ile ilgili olmadığını lütfen unutmayın!

Tamsayıların ve kayan noktaların taşmasının tanımsız davranış olduğunu belirten ilgili metin şudur:

C11 6.5 / 5

Bir ifadenin değerlendirilmesi sırasında (yani, sonuç matematiksel olarak tanımlanmamışsa veya türü için temsil edilebilir değerler aralığında değilse) istisnai bir koşul oluşursa, davranış tanımsızdır.

Özellikle imzasız tamsayı türlerinin davranışları hakkında bir açıklama burada bulunabilir:

C11 6.2.5 / 9

İşaretli bir tamsayı türünün negatif olmayan değerlerinin aralığı, karşılık gelen imzasız tamsayı türünün bir alt aralığıdır ve her türde aynı değerin temsili aynıdır. İmzasız işlenenleri içeren bir hesaplama hiçbir zaman taşamaz çünkü sonuçta işaretsiz tamsayı türü tarafından temsil edilemeyen bir sonuç, modüle, sonuçta elde edilen tür tarafından temsil edilebilecek en büyük değerden daha büyük bir sayıdır.

Bu, işaretsiz tam sayı türlerini özel bir durum haline getirir.

Ayrıca, herhangi bir tür işaretli bir türe dönüştürülürse ve eski değerin artık temsil edilemezse bir istisna olduğunu unutmayın . Davranış sadece uygulama tarafından tanımlanır, ancak bir sinyal yükseltilebilir.

C11 6.3.1.3

6.3.1.3 İmzalı ve imzasız tamsayılar

Tamsayı türündeki bir değer _Bool dışında başka bir tamsayı türüne dönüştürüldüğünde, değer yeni türle gösterilebiliyorsa, değişmez.

Aksi takdirde, yeni tür imzasızsa, değer, yeni türün aralığına gelinceye kadar, yeni türde temsil edilebilecek maksimum değerden bir kez daha fazla eklenerek veya çıkarılarak dönüştürülür.

Aksi takdirde, yeni tür imzalanır ve değer içinde temsil edilemez; sonuç uygulama tarafından tanımlanır veya uygulama tarafından tanımlanan bir sinyal oluşturulur.


6

Diğer konular yanında imzasız matematik sarma soyut cebirsel gruplar gibi (değerlerin herhangi çifti için, diğer şeyler arasında, yani davranmasına işaretsiz tamsayı tiplerini yapar sahip bahsedilen Xve Ydiğer bazı değer var olacaktır, Zöyle ki X+Zirade, düzgün döküm halinde , eşittir Yve Y-Zuygun şekilde dökülürse eşit olurX). İmzasız değerler yalnızca depolama yeri türleriyse ve ara ifade türleri değilse (örneğin, en büyük tamsayı türünün imzasız eşdeğeri yoksa ve imzasız türlerdeki aritmetik işlemler, onları ilk olarak daha büyük imzalı türlere dönüştürülmüş gibi davranırsa, o zaman orada tanımlanmış sarma davranışına çok fazla ihtiyaç duymaz, ancak ek bir tersi olmayan bir türde hesaplamalar yapmak zordur.

Bu, etrafı saran davranışın gerçekten yararlı olduğu durumlarda yardımcı olur - örneğin TCP sıra numaraları veya karma hesaplama gibi belirli algoritmalar. Ayrıca, taşmaları algılamanın gerekli olduğu durumlarda da yardımcı olabilir, çünkü hesaplamaları yapmak ve taşma olup olmadığını kontrol etmek, özellikle de hesaplamalar mevcut en büyük tamsayı türünü içeriyorsa, önceden taşma olup olmadığını kontrol etmekten genellikle daha kolaydır.


Tam olarak takip etmiyorum - neden katkı maddesi tersine sahip olmak yardımcı oluyor? Taşma davranışının gerçekten yararlı olduğu herhangi bir durumu gerçekten düşünemiyorum ...
sleske

@sleske: İnsan tarafından okunabilirlik için ondalık kullanılması, bir enerji ölçer 0003 okuyorsa ve önceki okuma 9995 ise, bu -9992 birim enerji kullanıldığı veya 0008 birim enerji kullanıldığı anlamına mı geliyor? 0003-9995 verim 0008'e sahip olmak, ikinci sonucun hesaplanmasını kolaylaştırır. -9992 vermesi onu biraz daha garip hale getirir. Bununla birlikte, bunu yapamamak, 0003 ila 9995'i karşılaştırmayı, daha az olduğunu fark etmeyi, ters çıkarma yapmayı,
9999'dan

@sleske: Hem insanlar hem de derleyiciler için, ifadeleri yeniden yazmak ve basitleştirmek amacıyla, birleştirici, dağıtıcı ve değişmeli aritmetik yasalarını uygulayabilmek çok yararlıdır; Örneğin, ifade eğer a+b-cbir döngü içinde hesaplanır, ancak bve co döngü içinde sabittir, bunun hesaplamasını taşımak yararlı olabilir (b-c)döngü dışında, ancak bunu yaparken diğer şeyler arasında gerektirecektir (b-c), eklenen bir değeri üretmek a, verim verecek a+b-cve bu da cters bir katkı maddesi gerektirmektedir .
Supercat

: Açıklamalar için teşekkürler. Bunu doğru anlarsam, örneklerinizin hepsi aslında taşmayı ele almak istediğinizi varsayar. Karşılaştığım çoğu durumda, taşma istenmeyen bir durumdur ve bunu önlemek istersiniz, çünkü taşma ile bir hesaplama sonucu yararlı değildir. Örneğin, enerji ölçer için muhtemelen taşma asla gerçekleşmeyecek bir tür kullanmak istersiniz.
sleske

1
... türünün aritmetik değerinin temsil edilip edilemeyeceğine (a+b)-ceşit olacak şekilde, ikame, olası değer aralığına bakılmaksızın geçerli olacaktır . a+(b-c)b-c(b-c)
Supercat

1

Belki de işaretsiz aritmetiğin tanımlanmasının başka bir nedeni, işaretsiz sayıların modulo 2 ^ n tamsayılarını oluşturmasıdır, burada n, işaretsiz sayının genişliğidir. İmzasız sayılar, ondalık basamaklar yerine ikili basamaklar kullanılarak temsil edilen tamsayılardır. Bir modül sisteminde standart işlemlerin gerçekleştirilmesi iyi anlaşılmıştır.

OP'nin alıntısı bu gerçeğe atıfta bulunur, ancak aynı zamanda ikilikte imzasız tamsayıları temsil etmek için tek, açık, mantıklı bir yol olduğu gerçeğini vurgular. Buna karşılık, İmzalı sayılar çoğunlukla ikisinin tamamlayıcısı kullanılarak temsil edilir, ancak standartta açıklandığı gibi başka seçenekler de mümkündür (bölüm 6.2.6.2).

İkinin tamamlayıcı gösterimi, bazı işlemlerin ikili biçimde daha anlamlı olmasını sağlar. Örneğin, negatif sayıları artırmak pozitif sayılarla aynıdır (taşma koşullarında beklenir). Makine seviyesindeki bazı işlemler işaretli ve işaretsiz sayılar için aynı olabilir. Bununla birlikte, bu operasyonların sonuçlarını yorumlarken, bazı durumlar mantıklı değildir - pozitif ve negatif taşma. Ayrıca taşma sonuçları, alttaki imzalı temsile bağlı olarak farklılık gösterir.


Bir yapının alan olması için, yapının katkı kimliği dışındaki her öğesinin çarpımsal bir tersi olmalıdır. Mod N tamsayılarının bir yapısı, sadece N bir veya asal olduğunda [N == 1 olduğunda degnerate alan] bir alan olacaktır. Cevabımda özlediğimi hissettiğin bir şey var mı?
supercat

Haklısın. Ana güç modülleriyle kafam karıştı. Orijinal yanıt düzenlendi.
yth

Burada daha kafa karıştırıcı olan , 2 ^ n sırasının bir alanı olması, modulo 2 ^ n tamsayılarına halka-izomorfik olmamasıdır.
Kevin Ventullo

Ve 2 ^ 31-1 bir Mersenne Prime'dır (ancak 2 ^ 63-1 asal değildir). Böylece orijinal fikrim mahvoldu. Ayrıca, tamsayı boyutları gün içinde farklıydı. Fikrim en iyi ihtimalle revizyonistti.
yth

İmzasız tamsayıların düşük dereceli kısmı alarak bir halka (alan değil) oluşturması da bir halka verir ve tüm değer üzerinde işlemler gerçekleştirir ve daha sonra kesme işlemi, sadece alt kısımdaki işlemleri gerçekleştirmeye eşdeğer davranır. neredeyse kesinlikle düşünceler.
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.