Kutuya sığmayan sayı
Ariane 5, 1996 — kopyalanan kodun varsayımı da kopyalanır
Bir sayı kendi kutusuna sığmadığında ne olduğunu kendi kartında görmek.
Ne oldu?
4 Haziran 1996. Avrupa Uzay Ajansı'nın yeni roketi Ariane 5 ilk uçuşunu yapıyor. İçinde dört bilim uydusu var. Roket insansız.
Kalkıştan kırk saniye bile geçmeden roket rotasından sapıyor, parçalanmaya başlıyor ve kendini imha ediyor.
Soruşturma kurulu sebebi buldu: roketin yönünü hesaplayan bilgisayarda, yatay hızla ilgili bir sayı kendisine ayrılan 16 bitlik kutuya sığmadı.
16 bitlik işaretli bir kutuya en fazla 32767 sığar. Kodun dili'nden hatırlıyorsun — Arduino UNO'daki int de tam olarak bu boyda.
Neden kimse fark etmedi?
Asıl ilginç kısım burası.
O kod, eski roket Ariane 4'ten kopyalanmıştı. Ariane 4'te o sayı hiçbir zaman 32767'ye yaklaşmıyordu. Mühendisler bazı sayıları taşmaya karşı korumuş, bazılarını korumamıştı — çünkü "bu sayı bu kadar büyüyemez" diye hesaplamışlardı.
Ariane 4 için doğru bir hesaptı. Ariane 5 ise daha hızlıydı.
Kod kopyalandı. Kodun içindeki varsayım da onunla birlikte kopyalandı. Kimse o varsayımı yeni roket için tekrar kontrol etmedi.
Bir ironi daha: taşan hesap kalkıştan sonra hiçbir işe yaramıyordu. Sadece kalkıştan önce gereken bir hesaptı, ama kalkıştan sonra da bir süre çalışmaya devam edecek şekilde bırakılmıştı.
Senin kartında
Bir roketin hızını her saniye ölçtüğümüzü düşünelim. Hız geniş bir kutuda — long — duruyor. Ama bir yerde, eski koddan kalma bir satır onu 16 bitlik dar bir kutuya koyuyor.
void setup() {
Serial.begin(9600);
long hiz = 0; // genis kutu: 2 milyara kadar sigar
for (int saniye = 0; saniye <= 16; saniye++) {
int16_t eskiKutu = hiz; // eski koddan kalma dar kutu
Serial.print("saniye ");
Serial.print(saniye);
Serial.print(" gercek hiz: ");
Serial.print(hiz);
Serial.print(" kutudaki: ");
Serial.println(eskiKutu);
hiz = hiz + 2500;
}
}
void loop() {
}
int16_t yeni bir şey değil: "tam olarak 16 bitlik int" demek. Kutunun boyunu açıkça yazıyoruz ki Arduino UNO'da da Deneyap'ta da aynı sonuç çıksın. Deneyap'ta düz int 32 bit — bu deneyi orada int ile yapsaydın hiçbir şey göremezdin.
Çalıştı mı?
Serial Monitor'ü aç, hız 9600 olsun. İlk satırlarda iki sayı aynı:
saniye 12 gercek hiz: 30000 kutudaki: 30000
saniye 13 gercek hiz: 32500 kutudaki: 32500
saniye 14 gercek hiz: 35000 kutudaki: -30536
saniye 15 gercek hiz: 37500 kutudaki: -28036
On dördüncü saniyede gerçek hız 35000, ama kutudaki sayı eksi oldu. Bu sistemin gözünde roket aniden geri gitmeye başladı.
Kod hata vermedi. Kart durmadı. Sadece sessizce yanlış bir sayıyla devam etti.
Ariane 5'te bir fark vardı: roketin yazılımı taşmayı fark etti ve bir hata verdi. Ama o hatayı karşılayacak bir kod yoktu, yönlendirme bilgisayarı kendini kapattı. Yedek bilgisayar da aynı yazılımı çalıştırdığı için o da aynı sebeple kapanmıştı. Bizim kart ise taşmayı fark bile etmiyor. İki sistem farklı şekilde başarısız oluyor ama sebep aynı: sayı kutusuna sığmadı.
Şimdi sen yaz
Diyelim ki eski kutuyu kaldıramıyorsun — kodun başka bir yeri ona bağlı. Ama içine sayı koymadan önce kontrol edebilirsin.
int16_t eskiKutu = hiz; satırını sil. Yerine şunu yaz: hız kutuya sığmayacaksa ekrana bir uyarı çıksın ve kutuya, sığabilecek en büyük sayı konsun.
// ŞİMDİ SEN YAZ:
// hiz 32767'den buyukse:
// "UYARI: sayi kutuya sigmiyor!" yaz ve eskiKutu'ya 32767 koy.
// Degilse eskiKutu'ya hiz'i koy.
🔍 İpucu 1
Önce kutuyu boş olarak tanımla: int16_t eskiKutu; — içine bir şey koymadan.
Sonra bir if ile iki yoldan birini seç. Kodun dili Görev 4'teki if ... else kalıbı.
✅ Cevap
int16_t eskiKutu;
if (hiz > 32767) {
Serial.println("UYARI: sayi kutuya sigmiyor!");
eskiKutu = 32767;
} else {
eskiKutu = hiz;
}
Buna kırpma deniyor: sığmayan sayıyı, sığabilecek en büyük değere sabitlemek. Doğru sayı değil — ama en azından yönü ters dönmüyor, ve uyarı sayesinde biri fark ediyor.
Kırpma her zaman doğru çözüm mü? Değil. Ariane 5 için gerçek çözüm, o hesabı kalkıştan sonra hiç çalıştırmamak ya da daha geniş bir kutu kullanmaktı. Ama "sığmazsa ne olacak?" sorusunu sormuş olmak, cevabın kendisinden önemli.
📄 Görevin tam kodu
void setup() {
Serial.begin(9600);
long hiz = 0;
for (int saniye = 0; saniye <= 16; saniye++) {
int16_t eskiKutu;
if (hiz > 32767) {
Serial.println("UYARI: sayi kutuya sigmiyor!");
eskiKutu = 32767;
} else {
eskiKutu = hiz;
}
Serial.print("saniye ");
Serial.print(saniye);
Serial.print(" gercek hiz: ");
Serial.print(hiz);
Serial.print(" kutudaki: ");
Serial.println(eskiKutu);
hiz = hiz + 2500;
}
}
void loop() {
}
Olmadıysa
| Ne görüyorsun | Ne yap |
|---|---|
| Ekran boş | Serial Monitor hızı 9600 mü? |
| Kutudaki sayı hiç eksiye düşmüyor | int16_t yerine int yazmışsın ve Deneyap kullanıyorsun. int16_t yaz |
'int16_t' was not declared |
Nadir görülür. En üste #include <stdint.h> ekle |
| Uyarı her satırda çıkıyor | > yerine < yazmışsın |
| Uyarı hiç çıkmıyor | if içinde hiz yerine eskiKutu'yu kontrol ediyorsun. Kutuya koymadan önce bakmalısın |
Kendini test et
Soru: Ariane 5'in mühendisleri dikkatsiz miydi?
Cevabı gör
Hayır. Ariane 4 için hesapları doğruydu. O sayı gerçekten de 32767'ye yaklaşmıyordu.
Hata, doğru bir varsayımın, artık doğru olmadığı bir yere taşınmasıydı. Kod kopyalandığında satırlar kopyalandı — ama "bu kod hangi hızlar için yazıldı?" bilgisi hiçbir satırda yazmıyordu.
Bu eğitimin en önemli dersi bu olabilir: bir kod parçasını kopyaladığında, sadece "çalışıyor mu" diye sorma. "Hangi koşullarda çalışacak şekilde yazıldı?" diye sor. İnternetten aldığın kod, senin koşullarını bilmiyor.
Akıllı Sera'da internetteki hazır eşik sayılarının sende çalışmamasının sebebi de tam olarak bu.
Bu hata nerede karşına çıkar
Afet haberleşme ağı kitindeki ivme sensörü ham ölçümlerini tam olarak 16 bitlik kutularda veriyor: −32768 ile 32767 arası. Sensörü çok sert sallarsan ölçüm aynı duvara çarpar. Kitin içinde bunu nasıl ele aldığımızı göreceksin.
Takıldın, hata tablosu da yetmedi mi?
WhatsApp'tan sor — hangi görevdesin, ekranda ne yazıyor, devrenin fotoğrafı.