← tüm yazılar
035

Go’da (Golang) Integer Bir Sayının Bitleri Üzerinde Nasıl Gezinilir?

Selamlar! Geçenlerde low-level bir iş üzerinde çalışırken bir int değişkeninin bitleri arasında tek tek gezinmem, yani iterate etmem gerekti. Kriptografi, performans optimizasyonları veya network paketleriyle uğraşıyorsanız yolunuzun buraya düşmesi anlık bir mesele.

Ben de bu süreçte Go’da bir tamsayının bitleri üzerinde nasıl efektif bir şekilde gezinebileceğimi tekrar bir kurcaladım. Karşıma çıkan ve işimi gören en yaygın 2 yöntemi ve standart kütüphane alternatifini buraya not düşmek istedim. İhtiyacınıza göre hangisini seçeceğiniz tamamen size kalmış.

1. Bit Shifting ve Maskeleme (En Klasik Yol)

Aklıma gelen ilk ve en yaygın yöntem bu oldu. Mantık çok basit: Sayıyı her adımda 1 bit sağa kaydırıyorum (>>) ve 1 ile AND (&) işlemine sokarak o anki bitin 0 mı yoksa 1 mi olduğunu kontrol ediyorum.

package main

import "fmt"

func main() {
    var num int = 42 // İkilik tabanda (binary): 101010

    // int türünün kaç bit olduğunu dinamik bulmak yerine
    // kolay okunsun diye şimdilik 32 bit üzerinden döndüm.
    bitSize := 32 

    fmt.Printf("%d sayısının bitleri (Sağdan sola): \n", num)

    for i := 0; i < bitSize; i++ {
        // En sağdaki biti (LSB) izole ettim
        bit := (num >> i) & 1
        fmt.Printf("Bit %d: %d\n", i, bit)
    }
}

Küçük Bir İpucu: Yukarıda yazdığım kod sağdan sola, yani en önemsiz bitten en önemli bite doğru (LSB‘den MSB‘ye) gidiyor. Eğer soldan sağa gitmek istersem, döngüyü tersine çevirip (num >> (bitSize - 1 - i)) & 1 şeklinde küçük bir düzeltme yapmam yetiyor.

2. Sabit Maskeyi Sola Kaydırmak

İlk yöntemde orijinal sayıyı sağa kaydırıyordum. Ama ya orijinal sayıyı hiç değiştirmek, kurcalamak istemiyorsam? Veya direkt belirli bir bit pozisyonuna nokta atışı bakmak istiyorsam?

İkinci yöntem olarak şunu denedim: Sayıyı sabit tutup, 1 değerine sahip bir maskeyi (mask) her adımda sola kaydırdım.

package main

import "fmt"

func main() {
    num := 42 // 101010

    fmt.Printf("%d sayısının bitleri (Soldan sağa): \n", num)

    // Anlaşılır olsun diye sadece son 8 bite bakmayı tercih ettim
    for i := 7; i >= 0; i-- { 
        mask := 1 << i
        if (num & mask) != 0 {
            fmt.Print("1")
        } else {
            fmt.Print("0")
        }
    }
    fmt.Println()
}

Bu yöntem özellikle bitmask işlemleri yaparken veya belirli flag’leri kontrol ederken temiz bir okunabilirlik sunuyor.

⚡ Bonus: Standart Kütüphane (math/bits)

Eğer amaç bitleri tek tek ekrana basmak veya döngüyle dönmek değil de sadece kaç tane 1 var veya başında kaç sıfır var gibi spesifik değerleri almaksa, Go’nun standart kütüphanesindeki math/bits paketi işi çözüyor.

Bu paket, assembly seviyesinde optimize edilmiş fonksiyonlar barındırıyor. Eğer donanım destekliyorsa direkt CPU talimatlarını (instructions) kullanarak çok hızlı sonuç veriyor.

package main

import (
    "fmt"
    "math/bits"
)

func main() {
    var num uint = 42 // math/bits genelde uint türleri ile çalışıyor, o yüzden uint yaptım

    // Kaç tane 1 olduğunun sayısını aldım
    fmt.Println("1 olan bit sayısı:", bits.OnesCount(num))

    // Toplam bit uzunluğunu aldım (öndeki 0'lar hariç)
    fmt.Println("Toplam bit uzunluğu:", bits.Len(num))
}

Eğer senin senaryonda sadece bu tarz hazır metrikler lazımsa, döngü yazmak yerine doğrudan bu fonksiyonları kullanıp geçmek mantıklı bir alternatif.

Yorum bırak