İşaretsiz tamsayı çıkarma davranış tanımlı mı?


100

Sonuç negatif olduğunda, işaretsiz bir tamsayıyı aynı türden başka bir tam sayıdan çıkarırken bir sorun olduğuna inanan birinden kodla karşılaştım. Yani bu tür bir kod, çoğu mimaride çalışsa bile yanlış olacaktır.

unsigned int To, Tf;

To = getcounter();
while (1) {
    Tf = getcounter();
    if ((Tf-To) >= TIME_LIMIT) {
        break;
    } 
}

Bu, bulabildiğim C standardından belli belirsiz alakalı tek alıntıdır.

İşaretsiz işlenenleri içeren bir hesaplama asla aşırı akış yapamaz, çünkü sonuçta ortaya çıkan işaretsiz tamsayı türüyle temsil edilemeyen bir sonuç, sonuçta ortaya çıkan türle temsil edilebilecek en büyük değerden bir büyük olan sayıya indirgenir.

Sanırım bu alıntı, doğru işlenen daha büyük olduğunda işlemin modulo kesilmiş sayılar bağlamında anlamlı olacak şekilde ayarlandığı anlamına gelebilir.

yani

0x0000 - 0x0001 == 0x 1 0000 - 0x0001 == 0xFFFF

uygulamaya bağlı işaretli semantiği kullanmanın aksine:

0x0000 - 0x0001 == (işaretsiz) (0 + -1) == (0xFFFF ve ayrıca 0xFFFE veya 0x8001)

Hangi veya hangi yorum doğrudur? Hiç tanımlanmış mı?


3
Standarttaki kelimelerin seçimi talihsizdir. "Asla taşamaz" demek, bunun bir hata durumu olmadığı anlamına gelir. "Sarmalar" değerini aşmak yerine standarttaki terminolojiyi kullanmak.
danorton

Yanıtlar:


107

İşaretsiz bir türden negatif bir sayı üreten bir çıkarma işleminin sonucu iyi tanımlanmıştır:

  1. [...] İşaretsiz işlenenleri içeren bir hesaplama asla taşamaz, çünkü sonuçta ortaya çıkan işaretsiz tamsayı türü ile temsil edilemeyen bir sonuç, sonuçta ortaya çıkan tür tarafından temsil edilebilecek en büyük değerden bir büyük olan sayı modulo azaltılır. (ISO / IEC 9899: 1999 (E) §6.2.5 / 9)

Gördüğünüz gibi (unsigned)0 - (unsigned)1-1 modulo UINT_MAX + 1'e veya başka bir deyişle UINT_MAX'a eşittir.

"İşaretsiz işlenenleri içeren bir hesaplama asla taşamaz" demesine rağmen, bu sizi yalnızca üst sınırı aşmak için geçerli olduğuna inanmanıza neden olabilir, bunun cümlenin gerçek bağlayıcı kısmı için bir motivasyon olarak sunulduğunu unutmayın : "a Sonuçta ortaya çıkan işaretsiz tamsayı türü ile temsil edilemeyen sonuç, sonuçta ortaya çıkan tür tarafından temsil edilebilecek en büyük değerden bir büyük olan sayı modulo azaltılmıştır. " Bu ifade, türün üst sınırının aşılmasıyla sınırlı değildir ve temsil edilemeyecek kadar düşük değerlere eşit ölçüde uygulanır.


2
Teşekkür ederim! Şimdi kaçırdığım yorumu görüyorum. Yine de daha net bir ifade seçebileceklerini düşünüyorum.

4
Ben sıfıra etrafında herhangi imzasız bir ek rulo ve kargaşa neden olursa, bunun nedeni olacağını bilerek, şimdi çok daha iyi hissediyorum uinthep matematiksel temsil amaçlı olduğunu halka tamsayılar 0yoluyla UINT_MAXtoplama ve çarpma modulonun operasyonları ile, UINT_MAX+1ve çünkü bir taşma. Bununla birlikte, halkalar bu kadar temel bir veri türü ise, dilin neden diğer boyutlardaki halkalar için daha genel bir destek sunmadığı sorusunu sormaktadır.
Theodore Murdock

2
@TheodoreMurdock Bence bu sorunun cevabı basit. Anladığım kadarıyla, bunun bir yüzük olması bir sebep değil, bir sonuçtur. Gerçek gereksinim, işaretsiz türlerin tüm bitlerinin değer temsiline katılması gerektiğidir. Yüzük benzeri davranış bundan doğal olarak ortaya çıkar. Diğer türlerden böyle bir davranış istiyorsanız, aritmetiğinizi ve ardından gerekli modülü uygulayarak yapın; temel operatörleri kullanan.
underscore_d

@underscore_d Elbette ... tasarım kararını neden verdikleri açık. Spesifikasyonu kabaca "veri türü halka olarak belirtildiğinden aritmetik taşma / eksiklik yoktur" şeklinde yazmaları eğlenceli, sanki bu tasarım seçimi programcıların aşırı ve az dikkatli olmaktan kaçınmak zorunda olmadıkları anlamına geliyormuş gibi -akma veya programlarının olağanüstü bir şekilde başarısız olmasını sağlama.
Theodore Murdock

121

İşaretsiz türlerle çalıştığınızda , modüler aritmetik ( "etrafı sarma" davranışı olarak da bilinir ) gerçekleşir. Bu modüler aritmetiği anlamak için şu saatlere bir göz atın:

görüntü açıklamasını buraya girin

9 + 4 = 1 ( 13 mod 12 ), yani diğer yöne göre: 1-4 = 9 ( -3 mod 12 ). İşaretsiz tiplerle çalışırken de aynı ilke uygulanır. Eğer sonuç türü olduğunu unsigned, daha sonra modüler aritmetik gerçekleşir.


Şimdi sonucu depolayan aşağıdaki işlemlere bakın unsigned int:

unsigned int five = 5, seven = 7;
unsigned int a = five - seven;      // a = (-2 % 2^32) = 4294967294 

int one = 1, six = 6;
unsigned int b = one - six;         // b = (-5 % 2^32) = 4294967291

Sonucun olduğundan emin olmak istediğinizde signed, onu bir signeddeğişkene kaydedin veya üzerine çevirin signed. Sayılar arasındaki farkı elde etmek istediğinizde ve modüler aritmetiğin uygulanmayacağından emin olmak istediğinizde, aşağıdaki bölümde abs()tanımlanan işlevi kullanmayı düşünmelisiniz stdlib.h:

int c = five - seven;       // c = -2
int d = abs(five - seven);  // d =  2

Özellikle koşulları yazarken çok dikkatli olun, çünkü:

if (abs(five - seven) < seven)  // = if (2 < 7)
    // ...

if (five - seven < -1)          // = if (-2 < -1)
    // ...

if (one - six < 1)              // = if (-5 < 1)
    // ...

if ((int)(five - seven) < 1)    // = if (-2 < 1)
    // ...

fakat

if (five - seven < 1)   // = if ((unsigned int)-2 < 1) = if (4294967294 < 1)
    // ...

if (one - six < five)   // = if ((unsigned int)-5 < 5) = if (4294967291 < 5)
    // ...

4
Saatlerle iyi olanı, ancak kanıt bunu doğru cevap yapacaktı. Sorunun öncülü zaten tüm bunların doğru olabileceği iddiasını içeriyor.
Orbit'te Hafiflik Yarışları

5
@LightnessRacesinOrbit: Teşekkürler. Yazdım çünkü birinin onu çok yararlı bulabileceğini düşünüyorum. Bunun tam bir cevap olmadığına katılıyorum.
LihO

4
Hat int d = abs(five - seven);iyi değil. İlk five - sevenolarak hesaplanır: terfi, işlem gören türlerini bırakır unsigned int, sonuç hesaplanmış modulo olur (UINT_MAX+1)ve değerlendirilir UINT_MAX-1. O zaman bu değer abs, kötü haber olan gerçek parametresidir . abs(int)aralık içinde olmadığından ve abs(long long)muhtemelen değeri tutabildiğinden bağımsız değişkeni geçiren tanımsız davranışa neden olur , ancak dönüş değeri intbaşlatılmaya zorlandığında tanımsız davranış oluşur d.
Ben Voigt

1
@LihO: C ++ 'da içeriğe duyarlı olan ve sonucunun nasıl kullanıldığına bağlı olarak farklı davranan tek operatör özel bir dönüştürme operatörüdür operator T(). Tartıştığımız iki ifadedeki toplama unsigned int, işlenen türlerine göre tipte gerçekleştirilir. Eklemenin sonucu unsigned int. Daha sonra bu sonuç dolaylı olarak bağlamda gereken türe dönüştürülür; bu, değer yeni türde gösterilemediği için başarısız olan bir dönüşümdür.
Ben Voigt

1
@LihO: double x = 2/3;vsdouble y = 2.0/3;
Ben Voigt

5

İlk yorum doğru. Ancak, bu bağlamda "imzalı anlambilim" hakkındaki mantığınız yanlış.

Yine, ilk yorumunuz doğru. İşaretsiz aritmetik, modulo aritmetiğinin kurallarını izler, yani 32-bit işaretsiz tipler için 0x0000 - 0x0001değerlendirilir 0xFFFF.

Bununla birlikte, ikinci yorumun ("işaretli anlambilim" e dayalı olan) da aynı sonucu üretmesi gerekir. Yani 0 - 1imzalı tipin etki alanında değerlendirseniz ve -1ara sonuç olarak elde etseniz bile , bunun daha sonra imzasız tipe dönüştürüldüğünde -1üretilmesi gerekir 0xFFFF. Bazı platformlar işaretli tamsayılar için egzotik bir temsil kullansa bile (1'in tamamlayıcısı, işaretli büyüklüğü), bu platformun işaretli tamsayı değerlerini işaretsiz değerlere dönüştürürken modulo aritmetiği kurallarını uygulaması gerekir.

Örneğin, bu değerlendirme

signed int a = 0, b = 1;
unsigned int c = a - b;

Hala üretmesi garanti edilir UINT_MAXyılında cplatformu imzalı tamsayı için egzotik bir temsilini kullanıyor olsa bile,.


4
Sanırım 16 bitlik işaretsiz türleri kastediyorsunuz, 32 bit değil.
xioxox

4

İşaretsiz sayılar unsigned intveya daha büyük tür dönüşümleri olmadığında, a-beklendiğinde ortaya bçıkacak işaretsiz sayıyı veren olarak tanımlanır a. Negatif bir sayının işaretsiz sayıya dönüştürülmesi, işareti ters çevrilmiş orijinal sayıya eklendiğinde sıfır veren sayıyı vermek olarak tanımlanır (bu nedenle -5'i işaretsiz'e dönüştürmek, 5'e eklendiğinde sıfır veren bir değer verir) .

İşaretsiz sayılardan daha küçük sayıların çıkarılmadan önce yazılmaya unsigned intyükseltilebileceğini unutmayın int, davranışı a-bboyutuna bağlı olacaktır int.

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.