Tüm yazılar
Programlama İpucu

Sayacım 32767'de Takıldı, Ertesi Gün Açtığımda Eksi Bir Sayı Gösteriyordu

Bir üretim sayacım 32767'de takılıp ertesi gün -28000 gösterdi. 16 bitlik bir INT'in neden 32768'de -32768'e sarıp geri döndüğünü, taşmayı (overflow) 'sayı büyüdü' sanmanın neden tehlikeli olduğunu ve REAL/DINT'e geçme kararını kendi vaka üzerinden anlatıyorum.

Sayacım 32767'de Takıldı, Ertesi Gün Açtığımda Eksi Bir Sayı Gösteriyordu

Çarşamba, gece vardiyasında değil — sabah vardiyasında, yedi buçukta. Vardiya değişiminde operatör bana “abi üretim sayacı eksi iki bin seksen gösteriyor, düştü mü ne oldu” dedi. Üretim sayacı, makinenin açıldığından beri ürettiği parça sayısını tutuyordu. Eksi olamazdı. Ben de “eksi olmaz, bakayım” dedim. Baktığımda sayacın 32767’den sarıp -28000’e geldiğini gördüm. Ama “sarıp” kelimesinin ne anlama geldiğini, o sabah, programı yeniden okurken öğrendim.

16 Bitlik bir INT, 32767’de Dolar

Sayacımı bir INT olarak tanımlamıştım. INT 16 bit. 16 bit, işaretli (signed) olduğunda -32768 ile +32767 arası tutar. Yani üst sınır 32767’di. Makine günde yaklaşık 30 bin parça üretiyordu. Birkaç gün sonra sayac, 32767’ye yaklaştı. Sonra, bir parça daha. 32768.

16 bitin 32768’i tutacak yeri yok. Bitler taşar: 0111 1111 1111 1111 (32767) bir artar, 1000 0000 0000 0000 olur. Bu bit deseni, işaretli 16 bitte -32768’dir. Sayac, 32767’den bir artınca, -32768’e “sardı.” Sonra üretime devam etti: -32767, -32766, … -28000. Operatörün gördüğü eksi sayı, bu sarmanın ortasıydı.

Program bir hata vermedi. Taşma, PLC’de sessizdir. Bitler akar, değer sarar, alarm yok. Çünkü sen, “sayı büyür” diye yazmıştın; oysa 16 bitte sayı, bir yere kadar büyür, sonra sınırın diğer ucundan geri gelir.

“Sayı Büyüdü” Sanmak, Taşmayı Gizler

Benim hatam, taşmayı “büyüme” ile aynı şey sanmaktı. “Parça ürettikçe sayac artar, sorun olmaz” dedim. Doğruydu — 32767’ye kadar. Ama “sonra ne olur”u sormamıştım. Sormadıysan, PLC senin yerine sorar; cevabı, -32768 ile verir.

Çözüm iki katmanlı:

Birincisi, sayacın tipi. Bu sayac, “bir vardiya” değil, “makinenin ömrü boyunca ürettiği parça”yı tutuyordu. O, 32767’e sığmaz. Bir DINT (32 bit, ±2.1 milyar) ya da REAL gerekir. DINT’e çevirdiğimde, 32767 sınırı 2 milyar 147 milyon 483 bin 647’ye çıktı. Makine, ömrü boyunca o sınırı göremez.

// Yanlış: 16 bit, 32767'de dolar
#uretim_sayaci : INT := 0;

// Doğru: 32 bit, ömür boyu sığar
#uretim_sayaci : DINT := 0;

İkincisi, sarmayı erken yakalamak. Tipi büyütmeden önce, “sayac yaklaşıyor” alarmı koy. if (#uretim_sayaci > 30000) alarm_sayac_dolu := true; O alarm, “sarma geliyor, sıfırla ya da tip büyüt” der. Sessizce -28000’e düşmek yerine, 30000’de seni uyarır.

Kural: Sayacın Tipini, “Nasıl Büyük Olabilir?” Sorusuyla Seç

Artık kuralım şu: bir sayac ya da bir birikimli değer tanımlarken, “bu değer en fazla kaç olabilir?” diye sor ve cevabı ikiyle çarp. Günde 30 bin üretiyorsa, “30 bin” değil, “ayda 900 bin, yılda 10.8 milyon” diye düşün. 10.8 milyon, 32767’i katlar. DINT. İki saniyelik bir tip değişikliği, ertesi sabahki “eksi iki bin seksen”i siler.

Bir de ek: birikimli değerleri sıfırlanabilir yap. Bir “vardiya başı sıfırla” butonu. Öyleyse sayac, her vardiya başı 0’dan başlar; o zaman INT bile yeter. Ya vardiya başı sıfırla, ya tip büyüt — ikisinden biri. İkisini de yapmazsan, sarma seni bekler.

O sabah, o “eksi iki bin seksen,” bir DINT ile gitti. Operatör “abi artık eksi göstermiyor” dedi. Ben de o an anladım ki, 16 bit bana bir sınır vermişti; ben de o sınırı “büyüme” sanmıştım. Sınır, bir duvar değil bir kapıdır: geçersen, karşıdaki sayı, eksi başlar.

Bir sonraki yazıda, “debi hesabımda zaman sıfır olduğunda NaN çıktı” diye anlatıyorum. Sıfıra bölmenin neden “sonsuz” değil “NaN” (sayı değil) ürettiğini, ve NaN’ın neden her karşılaştırmada “yanlış” sayılıp alarmı susturduğunu.