Ders 03 / 16
İşaretli Tam Sayılar ve İkiye Tümleyen
Negatif sayıların bit örüntüsü içinde kodlanması, ikiye tümleyen gösterim ve taşma davranışı.
İçindekiler
Önceki iki ders sayıları negatif olmayan kabul etti. Oysa donanımda eksi işaretini tutacak fazladan bir yer yoktur: 32 bitlik bir yazmaçta yalnızca 32 bit vardır. İşaret de değerin kendisiyle aynı örüntünün içinde kodlanmak zorundadır.
Bu dersin sorusu, bu kodlamanın nasıl yapılacağıdır. Birden çok yanıt mümkündür; ancak donanım neredeyse tek bir yanıtta birleşmiştir ve bunun nedeni, o yöntemin toplama devresini hiç değiştirmemesidir.
İki Sezgisel Yaklaşım ve Sorunları
İşaret-büyüklük (sign-magnitude) gösteriminde en soldaki bit işareti, kalan bitler
büyüklüğü taşır. Sekiz bitte için 0000 0101, için 1000 0101 yazılır. Bu
gösterim insan sezgisine yakındır ve iki sorunu vardır.
Birincisi, sıfırın iki gösterimi olur: 0000 0000 ve 1000 0000. İki örüntü aynı
değeri taşıdığında, eşitlik karşılaştırması bit karşılaştırmasından ibaret olmaktan
çıkar.
İkincisi ve daha ağırı: toplama artık düz toplama değildir. hesabı bit
düzeyinde yapıldığında 0000 0101 + 1000 0101 = 1000 1010 çıkar; bu örüntü işaret-
büyüklük yorumunda demektir, oysa sonuç sıfır olmalıydı. Donanımın işaretleri
ayıklayıp büyüklükleri karşılaştırması ve çıkarma yapması gerekir.
Bire tümleyen (one’s complement) gösteriminde negatif sayı, pozitifin tüm bitleri
ters çevrilerek elde edilir: için 1111 1010. Toplama düz toplamaya yaklaşır ancak
elde biti yeniden başa eklenmelidir; ayrıca sıfır yine iki gösterimlidir (0000 0000 ve
1111 1111).
İkiye Tümleyen
İkiye tümleyen (two’s complement) gösterimi bu iki sorunu da ortadan kaldırır. Tanımı tek cümleyle verilebilir: en soldaki bitin konum değeri negatiftir. bitlik bir örüntünün değeri şudur:
Sekiz bit için en soldaki bitin ağırlığı , diğerlerininki alışıldık biçimde ’dir. Örnekler:
| Örüntü | Hesap | Değer |
|---|---|---|
0000 0101 |
||
1111 1011 |
||
1111 1111 |
||
1000 0000 |
||
0111 1111 |
Sıfırın tek gösterimi vardır: 0000 0000. En soldaki bit hâlâ işareti ele verir —
ise sayı negatiftir — ama bu bit ayrı bir işaret alanı değil, değerin bir parçasıdır.
bitlik ikiye tümleyen aralığı şudur:
Aralık bakışımlı değildir: sekiz bitte temsil edilir, edilemez. Negatif tarafta bir değer fazladır, çünkü sıfır pozitif tarafın bir örüntüsünü harcar.
Negatifi Üretmek
Bir sayının negatifini bulmak için iki adım yeterlidir: tüm bitleri ters çevir, sonuca bir ekle.
için: 0000 0101 ters çevir 1111 1010 bir ekle
1111 1011. Sonuç, tablodaki örüntüsüdür.
Aynı işlem ’e uygulanınca ’e dönülür: 1111 1011 0000 0100
0000 0101. İşlem kendi tersidir.
Kuralın neden çalıştığı şöyle görülür: bir örüntü ile tersinin toplamı, tüm bitleri olan örüntüdür, yani . Buna bir eklenirse elde edilir; bitlik bir yazmaçta değeri sıfıra karşılık gelir. Dolayısıyla ile “ters çevir ve bir ekle” sonucu toplandığında sıfır çıkar — negatifin tanımı budur.
Tek istisna en küçük değerdir: üzerinde işlem uygulandığında yine 1000 0000
elde edilir. aralığın dışında olduğundan, bu değerin negatifi sekiz bitte yoktur.
Toplama Neden Tek Devre
İkiye tümleyenin donanım açısından belirleyici üstünlüğü şudur: işaretli toplama, işaretsiz toplamayla aynı işlemdir. Bitler toplanır, taşan elde biti atılır.
hesabı sekiz bitte:
0000 0101 (5) + 1111 1101 (-3) ------------- 1 0000 0010 (elde biti atılır) 0000 0010 (2)
Sonuç doğrudur ve bunun için işaretlere bakılmamıştır. Çıkarma da ayrı bir devre gerektirmez: hesabı, ’nin negatifi üretilip toplanarak yapılır. Karşılaştırma işlemleri de aynı toplayıcı üzerine kurulur.
Bu, gösterim seçiminin donanım karmaşıklığını doğrudan azalttığı somut bir örnektir. Sezgiye en yakın gösterim (işaret-büyüklük) en pahalı devreyi, sezgiye en uzak gösterim (ikiye tümleyen) en ucuz devreyi verir.
Taşma
Sabit genişlik, aralığın dışına çıkan sonuçların temsil edilememesi anlamına gelir. Sonuç kaybolmaz; aralığın öbür ucundan geri döner.
İşaretsiz yorumda sekiz bitte hesabı 1111 1111 + 0000 0001 = 1 0000 0000
verir; elde atıldığında geriye 0000 0000, yani kalır. Sayı büyümemiş, başa
sarmıştır.
İşaretli yorumda hesabı 0111 1111 + 0000 0001 = 1000 0000 verir; bu
örüntü demektir. İki pozitif sayının toplamı negatif çıkmıştır.
İki durum arasındaki fark, aynı bit işleminin farklı yorumlanmasından ibarettir. Donanım her iki taşmayı da ayrı bayraklarla bildirir; hangisinin anlamlı olduğuna, değerin işaretli mi işaretsiz mi yorumlandığını bilen program karar verir.
Taşmanın pratikteki sonucu, sayısal kodun sessizce yanlış sonuç üretebilmesidir. İki büyük pozitif sayının toplamının negatif çıkması, döngü sayaçlarının başa sarması ve dizi indekslerinin beklenmedik yerlere işaret etmesi bu sınıftandır. Bu davranışın güvenlik boyutu, siber güvenlik müfredatında ayrıca ele alınır.
Genişletme
Bir değer daha geniş bir tipe taşınırken boş kalan sol bitler doldurulmalıdır. Değerin korunması için doldurma kuralı yoruma bağlıdır:
- İşaretsiz değerlerde sol bitler sıfırla doldurulur (sıfır genişletme).
- İşaretli değerlerde sol bitler işaret bitinin kopyasıyla doldurulur (işaret
genişletme). Sekiz bitlik
1111 1011(), on altı bitte1111 1111 1111 1011olur ve değeri korunur.
Yanlış kural uygulanırsa değer sessizce değişir: sekiz bitlik örüntüsü sıfır
genişletmeyle on altı bitte 0000 0000 1111 1011, yani olur. Ters yönde, on altı
bitlik örüntüsü işaretsiz yorumlanırsa okunur. Bu, farklı genişlikteki
tipler arasında dönüşüm yapan kodun tipik hata kaynaklarından biridir.
Ortak Örneğin İşaretli Yorumu
Kursun ortak örneği 0x41424344 örüntüsünün en soldaki biti ’dır — ilk onaltılık
rakam 4, yani 0100. Bu nedenle örüntü, 32 bitlik işaretli yorumda da işaretsiz
yorumdakiyle aynı değeri taşır: .
Örüntünün ilk rakamı 8 olsaydı — yani 0x81424344 — en soldaki bit olur ve aynı
işlemler bambaşka bir değer verirdi:
Bit dizisi ile onun anlamı arasındaki mesafeyi bundan iyi gösteren bir örnek yoktur: tek bir bitin değişmesi, değeri iki milyarın üzerinde kaydırır.
Uygulamada Görmek
Python’un tam sayıları sabit genişlikte değildir; taşma olmaz, sayı büyür. Sabit genişlikli donanım davranışını gözlemlemek için maskeleme kullanılır:
def sabit_genislik(deger: int, bit: int = 8) -> int: """Değeri `bit` genişliğinde ikiye tümleyen yorumuna indirger.""" maske = (1 << bit) - 1 # 8 bit için 0xFF deger &= maske # fazla bitleri at isaret_biti = 1 << (bit - 1) # 8 bit için 0x80 if deger & isaret_biti: deger -= 1 << bit # en soldaki bitin ağırlığı negatiftir return deger print(sabit_genislik(0b11111011)) # -5 print(sabit_genislik(127 + 1)) # -128 (işaretli taşma) print(sabit_genislik(-1)) # -1 print((255 + 1) & 0xFF) # 0 (işaretsiz başa sarma) print(sabit_genislik(0x41424344, 32)) # 1094861636 print(sabit_genislik(0x81424344, 32)) # -2126363836
sabit_genislik işlevinin gövdesi, bu dersin tanımının doğrudan çevirisidir: önce
genişliğe sığmayan bitler atılır, sonra en soldaki bit için negatif ağırlık uygulanır.
Özet
- Sabit genişlikli bir gösterimde işaret, ayrı bir alanda değil, örüntünün içinde kodlanır.
- İkiye tümleyen gösterimde en soldaki bitin konum değeri negatiftir; bitin aralığı ’dir ve sıfırın tek gösterimi vardır.
- Bir sayının negatifi, tüm bitler ters çevrilip bir eklenerek üretilir; en küçük değer bu işlemin istisnasıdır.
- İşaretli toplama, işaretsiz toplamayla aynı devrede yapılır; ikiye tümleyenin tercih nedeni budur.
- Aralık aşıldığında sonuç kaybolmaz, öbür uçtan geri döner: işaretsizde , işaretlide .
- Genişletmede doldurma kuralı yoruma bağlıdır; işaretli değerlerde işaret biti kopyalanır.
Sonraki Adım
Tam sayılar, temsil edebildikleri aralık içinde kesindir: her değer ya vardır ya yoktur. Kesirli sayılarda durum başkadır — sonsuz sayıda gerçel değeri sonlu sayıda örüntüye sığdırmak gerekir. Sonraki ders, bunun nasıl yapıldığını ve toplamının neden tam olarak etmediğini ele alacak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.