JavaScript'te Tek Sayı Tipi (Neredeyse)
Çoğu dilde tam sayılar ve ondalıklı sayılar için ayrı tipler vardır. JavaScript ise tarihsel olarak tek bir tip sundu: Number. İster 42 yazın, ister 3.14 ya da -0.001 — hepsi aynı ilkel tipe karşılık geliyor: 64-bit IEEE 754 çift hassasiyetli ondalıklı sayı (double).
İşin güzel tarafı şu: int ve float arasında cast yapmaya gerek yok, 2^31'de overflow derdi yok. Ama Number'ın float tabanlı temsili bazı sürprizler getiriyor ve yeni başlayanlar bu sürprizlere sık sık takılıyor. Tam da Number'ın yetersiz kaldığı durumlar için 2020'de ikinci bir sayısal tip olan BigInt dile eklendi.
Floating-point sürprizi: 0.1 + 0.2 neden 0.3 değil?
Şunu bir çalıştır:
İlk satır 0.30000000000000004 yazdırır. İkincisi ise false döndürür. Bu, JavaScript'e özgü bir tuhaflık değil — Python, Java, C ve IEEE 754 kayan nokta standardını kullanan her dil aynı şekilde davranır.
Sebebi şu: 1/3'ün ondalık sistemde tam olarak yazılamaması gibi, 0.1 ve 0.2 de ikili sistemde tam olarak temsil edilemez. En yakın ikili yaklaşım saklanır ve küçük hatalar birikir. Zihinsel modeli şöyle kurun: ondalık içeren Number değerlerini, yazdığınız sayıya çok yakın yaklaşık değerler olarak düşünün.
Para işlemlerinde 19,99 TL'yi 19.99 olarak saklamayın. Onun yerine kuruş cinsinden tam sayı olarak — yani 1999 şeklinde — saklayın ve ekrana basarken biçimlendirin. Kayan nokta hatalarından kaçınmanın en iyi alışkanlığı budur.
Kayan Noktalı Sayıları Güvenli Karşılaştırma
Eşitlik kontrolü güvenilir olmadığı için, gerektiğinde belirli bir tolerans payıyla karşılaştırın:
Number.EPSILON, 1 ile ondan sonra temsil edilebilen sayı arasındaki en küçük farktır — yani 1 civarındaki değerler için makul bir varsayılan tolerans. Çok büyük veya çok küçük büyüklüklerde ise giriş değerleriyle orantılı ölçeklenen bir tolerans kullanmak isteyeceksin.
Güvenli tam sayı aralığı
Belli bir büyüklüğe kadar olan tam sayılar 64-bit float içinde tam olarak temsil edilir. Bu sınırı geçtiğinde hassasiyeti bit bit kaybetmeye başlarsın:
2^53 - 1, altındaki her tam sayının eksiksiz temsil edilebildiği son sayıdır. Bu eşiğin ötesinde, bazı tam sayılar Number tipinde var bile değildir — en yakın komşularına yuvarlanırlar. Veritabanından gelen 64-bit ID'leri düz JSON sayısı olarak parse ediyorsanız, sessiz sedasız veri bozulmasına davetiye çıkarmış olursunuz.
BigInt devreye giriyor
BigInt, keyfi hassasiyette tam sayılar için ayrı bir primitive tiptir. Oluşturmanın iki yolu var: ya tam sayı literal'inin sonuna n eklersiniz ya da BigInt(...) fonksiyonunu çağırırsınız:
BigInt'in bellekten başka bir üst sınırı yok. Şu durumlarda tam aradığınız şey:
2^53değerini aşan veritabanı ID'leri veya Twitter/X snowflake ID'leri.- Kriptografik hesaplamalar.
- Hızdan çok kesin sonucun önemli olduğu tamsayı aritmetiği.
Ama gündelik sayaçlar, dizi indeksleri ya da parayı kuruş cinsinden tutmak için BigInt'e gerek yok — bu tip işlerde normal Number hem daha hızlı hem de dildeki neredeyse her API ile uyumlu çalışır.
BigInt ile aritmetik işlemler
Her iki operand da BigInt olduğu sürece alışılmış operatörlerin hepsi çalışır:
Bölme işlemi sıfıra doğru kırpılır — yani kesirli BigInt diye bir şey yok. Kesir gerekiyorsa yine Number dünyasına dönmen lazım (ya da decimal desteği veren bir kütüphane kullanman gerek).
Tipleri Karıştırma
İnsanların en çok takıldığı kural şu: aynı ifade içinde Number ve BigInt tiplerini bir arada kullanamazsın.
Karşılaştırma tek istisna — <, >, == operatörleri iki tür arasında otomatik dönüşüm yapar:
== onları eşit sayar, === saymaz. Zaten her yerde === kullanıyorsan (ki kullanmalısın), iki tip arasındaki sayısal karşılaştırmaları bir tasarım kokusu olarak gör — bir tarafı seç ve dönüştür.
İki Tip Arasında Dönüşüm
İki dönüşüm yolu var, her birinin kendi tuzağı:
Number → BigInt dönüşümü katıdır: ondalıklı değerler ve NaN hata fırlatır. BigInt → Number yönü ise esnek ama kayıplıdır — MAX_SAFE_INTEGER'ın üzerindeki her şey yuvarlanır. Sunucudan aldığın bir BigInt'i dönüştürüyorsan, buna gerçekten ihtiyacın olup olmadığını bir kez daha düşün.
Özel Number değerleri
Hazır konu açılmışken, Number tipinde yer alan ama matematiksel anlamda "sayı" sayılmayan üç değerden bahsedelim:
Infinity ve -Infinity değerleri, sıfıra böldüğünüzde ya da float aralığını taşırdığınızda karşınıza çıkar. NaN ("not a number" yani "sayı değil") ise bir aritmetik işlem anlamlı bir sonuç üretmediğinde ortaya çıkar.
NaN'in meşhur bir özelliği var: kendisine bile eşit değildir. Bu bir JS bug'ı değil, IEEE 754 standardının gereği. Kontrol etmek için Number.isNaN(x) kullanın. Eski global isNaN ise argümanını önce sayıya çevirdiği için yanlış sonuçlar verir — mesela isNaN("hello") true döner. Her zaman Number.isNaN versiyonunu tercih edin.
String'den Sayıya Dönüşüm
Kullanıcı girdileri ve JSON verilerindeki sayılar çoğu zaman string olarak gelir. Dönüştürmenin üç yolu var:
Number() oldukça katıdır — sayısal olmayan her şey için NaN döner, yalnız boş string ve sadece boşluk içeren string 0 verir. parseInt ve parseFloat ise toleranslıdır — okuyabildiği kadar okur, takıldığı yerde durur. Amacınıza hangisi uyuyorsa onu seçin ve sonucu kullanmadan önce NaN kontrolü yapın.
String'den BigInt elde etmek için BigInt("123") kullanın — bu yöntem katıdır ve geçersiz girdide hata fırlatır.
Hızlı Kural Kitabı
- Sayaçlar, matematiksel işlemler, koordinatlar ve günlük sayıların çoğu için:
Numberkullanın. - Para için: tam sayı cinsinden kuruşa çevirip
Numberkullanın veya bir decimal kütüphanesine başvurun. 2^53'ten büyük tam sayılar için (veritabanı ID'leri, kriptografi, kombinatorik):nson ekiyleBigIntkullanın.- Float'ları karşılaştırırken
===yerine bir tolerans payı kullanın. - Geçersiz sonuçları global sürümlerle değil,
Number.isNaNveNumber.isFiniteile kontrol edin. - Aynı ifadede
NumberileBigInt'i karıştırmayın — dönüşümü açıkça yapın.
Sırada: null vs undefined
JavaScript'te "değer yok" demenin iki yolu vardır — null ve undefined — ve bunlar birbirinin yerine kullanılamaz. Sırada: her birinin ne anlama geldiği, aralarındaki fark ve hangisini ne zaman tercih etmeniz gerektiği.
Sıkça Sorulan Sorular
JavaScript'te neden 0.1 + 0.2 sonucu 0.3 etmiyor?
Çünkü JavaScript'teki Number tipi 64-bit IEEE 754 floating-point formatında tutuluyor ve 0.1 ile 0.2 sayıları ikilik tabanda tam olarak ifade edilemiyor. Sonuç 0.30000000000000004 çıkıyor. Bu bir JavaScript bug'ı değil — Python, Java ve aynı float formatını kullanan her dilde aynı sorun var. Para hesapları için sayıyı tam sayıya ölçekleyin (örneğin kuruşa çevirin) ya da decimal bir kütüphane kullanın.
BigInt nedir ve ne zaman kullanmalıyım?
BigInt, Number.MAX_SAFE_INTEGER (2^53 - 1) sınırının ötesindeki tam sayılar için ayrı bir sayısal primitive tip. Sonuna n ekleyerek (9007199254740993n gibi) ya da BigInt(value) çağrısıyla oluşturabilirsiniz. 64-bit veritabanı ID'leri, kriptografi veya hızdan çok hassasiyetin önemli olduğu her tam sayı hesabında kullanın.
JavaScript'te Number ve BigInt'i birlikte kullanabilir miyim?
Hayır. 1n + 1 ifadesi TypeError: Cannot mix BigInt and other types hatası fırlatır. Dönüşümü elle yapmanız gerekir: BigInt(n) veya Number(b). < ve == gibi karşılaştırma operatörleri iki tip arasında çalışır ama === tipler farklı olduğu için false döner.