Make love, not var_dump())

Captain's Log

İlyas Özgören, Özgür Web Teknolojileri Günleri'nde bir soru daha sormuştu. JavaScript'te "büyük sayılarla" ilgili bir problemi olduğunu söylüyordu. Sorunu tam anlamamıştım.

var x = 12345678923456789123456789; //x büyük bir rakam (64 bitten daha büyük integer)
var y = x + 10; //bu rakama 10 ekleyip 12345678923456789123456799 rakamına ulaşmak istiyorum

console.log(x); //1.234567892345679e+25

console.log(y); //1.234567892345679e+25 ?? aynı çıktı değil mi?

console.log(x == y); //true  ??? nasıl true olur ya?

Daha önce böyle bir olayla karşılaşmamıştım. (bkz: 6 yaşımdan beri JavaScript kodlarım :p). Bu konuda daha önce bir deneyimim olmadığını belirtip, araştırayım, blog'a yazarım dedim :)

JavaScript'te her rakam, aslında 64 bit ile tanımlanan bir floating point. Bu tanımlama IEEE_754'e göre yapılıyor. Kabaca, ilk bit "sign" dediğimiz pozitif negatif değer için "flag", 11 bitlik bir "exponent" tanımı ve geri kalan 52 bit için de "fraction" dediğimiz sayıları tanımlayabileceğimiz alandan oluşuyor. Bu tanımlamanın geniş detayını http://www.2ality.com/2012/04/number-encoding.html adresinden inceleyebilirsiniz.

Özet geçecek olursak, JavaScript'te 53. bitin de dolu olduğu 9007199254740992 sayısından daha büyük sayılar için aritmetik işlemler yapacaksak, dikkatli olmamız gerektiği. Zira bundan daha yüksek sayılarda yapacağımız işlemlerde istediğimiz sonucu alamayacağız. Örneğin
var x = 9007199254740992 + 1;  //9007199254740993 olmasını bekliyoruz değil mi?
console.log(x); //9007199254740992 ???

"Peki ama JavaScript'te büyük sayılarla çalışmamız gerekirse ne yapabiliriz?"


Bunun için çeşitli kütüphaneler var, bunlardan benim incelediğim bir tanesi Matthew Crumley'un geliştirdiği BigInteger kütüphanesi.
Kodlarına Github üzerinden ulaşabileceğiniz bu kütüphane, sizin için aritmetik işlemleri, büyük sayılar için de yapıyor. Yukarıdaki örnek için
var x = BigInteger("9007199254740992").add("1").toString(); //9007199254740993 olmasını bekliyoruz yine
console.log(x); //9007199254740993 :) 

JavaScript'te operator overloading olmadığı için maalesef + - gibi operatörleri kullanamıyoruz ancak onun yerine bize "add", "subtract" gibi API'lar sağlıyor. Tüm API dokümantasyonuna http://silentmatt.com/biginteger-docs/files/biginteger-js.html adresinden ulaşabilirsiniz.

Yukarıdaki kodda, dikkat ettiyseniz BIgInteger sınıfına verdiğimiz parametreyi integer olarak değil, tırnaklar arasında "string" olarak geçtik. Bunu yapmamızın sebebi de, input olarak verilecek sayının, yukarıda anlattığımız 11 bitlik exponent değerini de aşması gibi durumlarda oluşacak hataları önlemek.

Bu konuyu öğrendikten sonra, Twitter API'ında, isteklere şu şekilde dönen cevaplarda
{... ,
"id": 187462027801919500,
"id_str": "187462027801919489", 
...}
"id" değerini, neden bir de "id_str" içinde string olarak döndüklerini daha iyi anlamış oldum. Bu konu hakkındaki daha detaylı bilgi https://dev.twitter.com/discussions/7041 adresinde mevcut.

Günün sonunda, JavaScript'te büyük rakamlar ile uğraşacaksanız dikkatli olmalısınız. Eğer yine de uğraşmanız gerekecekse, BigInteger gibi bir kütüphane kullanmalısınız.