İçeriğe geç
academia.sh

Ders 07 / 21

Büyük Tam Sayılar

Güvenli tam sayı aralığının aşıldığı durumlar, keyfi büyüklükte tam sayı tipi, işleç davranışı, tip karıştırma yasağı ve dizileştirme sınırı.

İçindekiler

Önceki ders, sayı tipinin tamamının IEEE 754 çift duyarlıklı gösterim olduğunu ve bu gösterimin tam sayılar için 25312^{53}-1 değerinde bir sınır taşıdığını gösterdi. Sınırın üstünde iki farklı tam sayı aynı bit örüntüsüne yuvarlanır ve eşit çıkar.

Bu ders o sınırın gerçekten aşıldığı durumları ve dilin buna verdiği yanıtı ele alır: keyfi büyüklükte tam sayı taşıyan ayrı bir ilkel tip. Tipin kendisi kadar, sayı tipiyle arasına konan kesin sınır da önemlidir.

Sınır Nerede Aşılır

Güvenli aralık yaklaşık dokuz katrilyondur. Günlük hesaplarda uzak görünen bu değer, belirli veri türlerinde tek bir okumayla geçilir:

  • Nanosaniye çözünürlüklü zaman damgaları. Bir başlangıç anından itibaren geçen nanosaniye sayısı, milisaniyeye göre bir milyon kat büyüktür ve aralığın dışına çıkar.
  • Başka sistemlerden gelen 64 bitlik tam sayı kimlikleri. Kimlik üzerinde aritmetik yapılmasa bile, değeri kaybetmeden okuyup geri yazmak gerekir.
  • Uzun süre biriken sayaçlar. Yüksek örnekleme hızlı bir ölçüm ağında toplam örnek sayısı bu aralığa ulaşabilir.

Üçünde de ortak nokta, hassasiyetin son basamağa kadar korunması gerektiğidir. Sayı tipiyle çalışıldığında kayıp sessizdir; hata verilmez, yalnızca yanlış değer taşınır.

Ayrı Bir İlkel Tip

Büyük tam sayı (BigInt), keyfi basamak sayısında tam sayı taşıyan ilkel tiptir. İki biçimde yazılır: sayı yazımının sonuna n eklenerek veya bir dönüştürme çağrısıyla.

const a = 9007199254740993n;
const b = BigInt("9007199254740993");
console.log(a === b);
console.log(typeof a);
console.log(a + 1n);
true
bigint
9007199254740994n

Önceki derste bu değer sayı tipiyle temsil edilemiyordu; burada tam olarak korunuyor. Çıktıdaki n soneki değerin parçası değildir, çalışma zamanının inceleme biçimidir. Değer metne çevrildiğinde sonek görünmez:

console.log(String(10n));
console.log(10n.toString());
console.log(10n);
10
10
10n

İlk iki satır dilin metne çevirme kuralının sonucudur ve her ortamda aynıdır. Üçüncü satırdaki gösterim ortamın inceleme biçimidir. Kod Çalıştırma Yolları dersinde kurulan ayrım burada tekrar karşımıza çıkıyor.

İşleç Davranışı

Aritmetik işleçler tam sayı anlambilimiyle çalışır. En belirgin fark bölmededir:

console.log(7n / 2n);
console.log(7n % 2n);
console.log(2n ** 64n);
3n
1n
18446744073709551616n

Bölme kesir üretmez; sonuç sıfıra doğru kırpılır. Programlama Temelleri kursundaki Temel Veri Tipleri dersinde iki bölme işleminin ayrımı kurulmuştu — burada büyük tam sayı tipinde yalnızca tam bölme vardır. Kırpma yönü sıfıra doğrudur, aşağı doğru değil; negatif işlenenlerde bu ayrım sonuç değiştirir.

Üstel işlem, sayı tipinde temsil edilemeyecek büyüklükleri tam olarak verir. 2642^{64} değeri yirmi basamaklıdır ve son basamağına kadar doğrudur.

Tipler Karıştırılamaz

İki sayı tipi aritmetik işleçlerde bir araya gelemez:

try {
  console.log(1n + 1);
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
TypeError: Cannot mix BigInt and other types, use explicit conversions

Bu yasak bir eksiklik değil, bilinçli bir tasarım kararıdır. İki tipin anlambilimi uyuşmaz: sayı tipinde bölme kesir üretir, büyük tam sayıda üretmez; sayı tipinde büyük değerler yuvarlanır, büyük tam sayıda yuvarlanmaz. Karışık bir ifadenin hangi kuralla değerlendirileceği tek biçimde seçilemez. Dil, seçimi program yazarına bırakır ve açık dönüşüm ister.

Karşılaştırma işleçleri farklı davranır. Değer karşılaştırması yapan işleçler iki tipi birlikte ele alabilir; tip de karşılaştıran işleç ayırır:

console.log(1n == 1);
console.log(1n === 1);
console.log(2n > 1);
true
false
true

İlk ve üçüncü satırlar matematiksel değeri karşılaştırır. İkinci satır ise tipleri de karşılaştırdığı için yanlış verir. Bu üç işlecin kuralları Eşitlik Karşılaştırmaları dersinde adım adım tanımlanacak.

Dönüşümün Bedeli

İki tip arasında dönüşüm açıkça yazılabilir, ama her yön güvenli değildir.

Büyük tam sayıdan sayıya dönüşüm, aralık dışındaki değerlerde duyarlık kaybeder:

const zamanNs = 1700000000123456789n;
console.log(Number(zamanNs));
console.log(BigInt(Number(zamanNs)));
1700000000123456800
1700000000123456768n

İki satır birlikte okunmalıdır. Birinci satır, dönüşümden çıkan sayının metne çevrilmiş halidir; metne çevirme, aynı bit örüntüsüne geri dönen en kısa onluk gösterimi seçer, bu yüzden yuvarlak görünür. İkinci satır aynı değeri kayıpsız bir tiple yazar ve gerçek değeri gösterir: ...768. Yani hem özgün değer kaybolmuştur hem de kaybın miktarı ilk bakışta göründüğünden farklıdır.

Sayıdan büyük tam sayıya dönüşüm ise kesirli değerleri reddeder:

try {
  console.log(BigInt(1.5));
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
RangeError: The number 1.5 cannot be converted to a BigInt because it is not an integer

Sayı tipiyle çalışan yerleşik yöntemler de büyük tam sayı kabul etmez:

try {
  console.log(Math.max(1n, 2n));
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
TypeError: Cannot convert a BigInt value to a number

Math nesnesinin tamamı kayan noktalı sayılar için tanımlıdır. Büyük tam sayılarla karşılaştırma yapmak gerekiyorsa karşılaştırma işleçleri kullanılır.

Ölçüm Betiğine Uygulama

Ölçüm kayıtlarına nanosaniye çözünürlüklü zaman damgası eklendiğinde, iki tip aynı kayıtta yan yana bulunur:

const okumalar = [
  { istasyon: "A1", zamanNs: 1700000000123456789n, sicaklik: 21.5 },
  { istasyon: "A1", zamanNs: 1700000000923456789n, sicaklik: 21.7 },
];

const fark = okumalar[1].zamanNs - okumalar[0].zamanNs;
console.log("fark (ns):", fark.toString());
console.log("fark (ms):", (fark / 1000000n).toString());
fark (ns): 800000000
fark (ms): 800

Zaman farkı nanosaniye duyarlığında hesaplandı, sonra milisaniyeye çevrildi. Çevrimde kesir atıldı — tam bölme kırpma yapar. Kesirli bir sonuç gerekiyorsa, bölme sayı tipine geçilerek yapılır ve bu noktada duyarlık kaybı bilinçli olarak kabul edilir.

Sıcaklık alanı sayı tipinde kaldı; kayan noktalı bir büyüklük için doğru seçim odur. İki tipin aynı kayıtta bulunması sorun değildir — sorun, ikisini aynı ifadede karıştırmaktır.

Dizileştirme Sınırı

Büyük tam sayılar, verinin JSON biçiminde yazılmasını engeller:

try {
  console.log(JSON.stringify({ sayac: 10n }));
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
console.log(JSON.stringify({ sayac: String(10n) }));
TypeError: Do not know how to serialize a BigInt
{"sayac":"10"}

Neden bir eksiklik değil, biçimin tanımıdır: JSON’un sayı gösteriminin duyarlık garantisi yoktur ve okuyan taraf değeri sayı tipine çevirebilir. Değeri kayıpsız aktarmak için metin olarak yazmak, biçimin sınırını kabul eden çözümdür. JSON’un sınırları ayrı bir dersin konusudur.

Özet

  • Güvenli tam sayı aralığı nanosaniye zaman damgaları, 64 bitlik dış kimlikler ve uzun süre biriken sayaçlarda aşılır; kayıp sessizdir.
  • Büyük tam sayı ayrı bir ilkel tiptir; n sonekiyle veya açık dönüşümle yazılır ve keyfi basamak sayısı taşır.
  • Bölme kesir üretmez, sıfıra doğru kırpar; Math yöntemleri bu tiple çalışmaz.
  • Aritmetik işleçlerde iki sayı tipi karıştırılamaz; karşılaştırma işleçleri değer karşılaştırmasında birlikte çalışır, tip karşılaştıran işleç ayırır.
  • Büyük tam sayıdan sayıya dönüşüm aralık dışında duyarlık kaybettirir; JSON’a doğrudan yazılamaz, metin olarak aktarılır.

Sonraki Adım

Bu ders, iki tip arasındaki dönüşümün açıkça yazılmasını gerektiren tek durumu gösterdi. Dilin geri kalanında durum tersidir: tipler çoğu işleçte kendiliğinden dönüştürülür ve sonuç, dönüşümün hangi sırayla yapıldığına bağlıdır. Sonraki ders bu örtük zorlamanın adımlarını tanımlayacak; [] + {} gibi ifadelerin sonucu ezber değil, izlenebilir bir yordamın çıktısı olarak okunacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat