ilkel tür etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
ilkel tür etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

15 Nisan 2011 Cuma

boolean Türü ve İlkel Türler Arasında Tür Uyumluluğu

İlkel türleri tanıtmaya devam ettiğimiz bu yazıda, çalışmakta olan programda bir koşulun oluşup oluşmadığını denetlemek amacıyla kullanılan boolean türüne baktıktan sonra ilkel türden değerlerin birbirleri yerine kullanılabilme özelliklerine göz atacağız.

boolean Türü

Koşullu ve yinelemeli komut işlemenin temeli olan doğruluk denetimi, boolean türlü sabitler olan true veya false değerlerinden birini üreten ilişkisel işleçlerin1 kullanılmasıyla yapılır. Denetlenmesi istenen koşulun karmaşıklaşması durumunda ilişkisel işleçli ifadeler mantıksal işleçler2 kullanılarak birleştirilebilir.3 Bir örnekle görelim.

Faktöryel.java
import java.util.Scanner;

public class Faktöryel {
  public static void main(String[] ksa) {
    Scanner grdKnl = new Scanner(System.in);
    System.out.print("Faktöryeli bulunacak sayı: ");
    int n = grdKnl.nextInt();
    System.out.println(n + "!: " + fakt(n));
  } // void main(String[]) sonu

  public static long fakt(int n) {
    if (n == 1 || n == 0)
      return 1;
      else return n * fakt(n-1);
  } // long fakt(int) sonu
} // Faktöryel sınıfının sonu

Programımız, standart girdi dosyasından—değiştirilmediği müddetçe klavye—girilen karakterleri tarayarak istenen türden veriler haline dönüştüren Scanner türündeki grdKnl kullanılarak alınan tamsayı değerin faktöryelini hesaplıyor. Bu amaçla yazılmış olan özyinelemeli fakt metodunun sonsuz döngüye girmesini engellemekte kullanılan koşul deyimi, eşitlik denetleme işleci (==) ile oluşturulan iki altdeyimin veya bağlacı (||) ile birleştirilmesiyle oluşturuluyor. Buna göre, metottan parametre değerinin 0 veya 1'e eşit olması durumunda 1, aksi takdirde parametrenin 1 küçük değerin faktöryeli ile çarpımı döndürülüyor.

Mantıksal türün sonradan eklendiği C'nin yerleştirdiği alışkanlıklar sonucu, C/C++'da bir koşulun oluşmama durumu sıfır-benzeri bir değerle (NULL, 0,0, 0, vd.) temsil edilebilirken, oluşma durumu sıfır-benzeri olmayan herhangi bir değerle temsil edilebilir. Tür güvenliğine önem veren Java'da bu alışkanlığın unutulması gerekecektir. Dolayısıyla, aşağıdaki kod parçası Java derleyicisi tarafından kabul edilmez.

if (n % 2)
  System.out.println(n + " çift değil");
  else System.out.println(n + " çift");

İlkel Türler Arasında Tür Uyumluluğu

C/C++ dünyasından aşina son satırların Java derleyicisi tarafından kabul görmemesi, tamsayıların boolean değerler ile tür uyumlu olmadığı şeklinde ifade edilir. C/C++ dilinin "Programcı hata yapmaz, hata gibi gözüken şey muhtemelen yaratıcı bir kestirmedir" anlayışına karşılık, "Programcı da bir insandır ve hata yapabilir" felsefesinden hareket eden Java, belirtiyi ciddiye alır ve olası hatanın başkalaşmasına izin vermeden programcıya en erken aşamada (derleme) durumu bildirmeyi tercih eder.

Tamsayıların mantıksal değerlerin yerine kullanılamamasına benzer bir şekilde, byte türlü bir tanımlayıcıya int türlü bir değer sağlanması da hata olarak görülecektir. Bunun sebebi, int türünün temsil aralığının byte türünün temsil aralığında bulunmayan değerler içermesidir. Mesela, yukarıda verilen Faktöryel.fakt metodunun parametre türü byte olacak biçimde değiştirilmesi hataya neden olacaktır. Çünkü, söz konusu metodun çağrıldığı satırda geçirilen argüman int türlüdür ve -231 ile 231-1 arasındaki herhangi bir değere sahip olabilir; buna karşılık, argümanın değeriyle ilklenecek parametre byte türlüdür ve -128 ile 127 arasındaki değerleri temsil edebilir. İşte bu yüzdendir ki, parametre türünün byte olarak değiştirilmesi durumunda geçirilen argüman değerinin aşağıdaki kod parçasında olduğu gibi biçimlendirilmesi gerekir.

import java.util.Scanner;

public class Faktöryel {
  public static void main(String[] ksa) {
    ...
    System.out.println(n + "!: " + fakt((byte) n));
  } // void main(String[]) sonu

  public static long fakt(byte n) { ... } // long fakt(int) sonu
} // Faktöryel sınıfının sonu

Pek çok—kesin olmak gerekirse, sekiz—ilkel türün varlığı, tür uyumluluğu konusunda karar verme işini zorlaştırabilir. Bu noktada, türlerin kayan noktalı ve tamsayı türler şeklinde dizilmesi imdadınıza yetişecektir.

doublefloatlongintshortbyte
intchar
doubleboolean = ∅

Yukarıdaki geçişken üstküme-altküme ilişkisi ile resmedilen dizilim, en baştaki türün en kapsayıcı en sondakinin ise en az kapsayıcı olduğu şeklinde yorumlanmalıdır.4 Buna göre, double değerin beklendiği yerlerde diğer tüm sayısal türlerden değerler kullanılabilir. Buna karşılık, short değerin beklendiği yerlerde sadece byte türlü değerler kulanılabilirken, diğer tüm sayısal değerler ancak biçimlendirme yapılmak suretiyle kullanılabilecektir.

Dizilimde char türü için ayrı bir kapsama ilişkisinin tanımlanması, bu türün tamsayı türü olarak ele alınması durumunda eksi sayıları temsil edemeyecek olmasından kaynaklanır. Aynı büyüklükte bir alan kaplayan short -32768 ile 32767 aralığındaki değerleri temsil edebilirken, char 0 ile 65536 arasındaki değerleri temsil edebilir. Bir diğer deyişle, short'un char, char'ın short yerine konulması kimi zaman değer kaybına sebep olacaktır.


  1. Yararlanılabilecek ilişkisel işleçler ve varlığı denetlenen ilişki çeşidi şunlardır: == (eşitlik), != (eşitsizlik), > (büyüklük), < (küçüklük), >= (büyük veya eşitlik), < (küçük veya eşitlik).
  2. Mantıksal değerleri birleştirmede kullanılabilecek bağlaçlar ve anlamları şunlardır: ! (değilleme), && (ve), || (veya).
  3. Gerekli görüldüğü takdirde, denetleme işinin tümü veya bir bölümü, yüklem olarak da adlandırılan boolean dönüş türlü bir metodun çağrılması ile yapılabilir.
  4. Aslına bakacak olursanız, tür uyumluluğu için üstküme-altküme ilişkisinin kullanılması yanıltıcı bir uygulama. Çünkü, long ile temsil edilen kimi değerler ne double ne de float ile temsil edilebilirken, int ile temsil edilen kimi değerler float ile temsil edilemez. Ancak, anılan türler arasında dizilimin önerdiği cinsten bir tür uyumluluğundan bahsetmek her zaman doğrudur.

6 Nisan 2011 Çarşamba

İlkel Türler-Kayan Noktalı Sayılar

İlkel türler hakkındaki dizimizin üçüncü yazısında, matematikteki gerçel sayıların temsil edilmesinde kullanılan kayan noktalı sayı türlerine göz atacağız. Tamsayı türlerinin anlatıldığı yazıdan🔎 da tanıdık gelecek çoğul eki bu amaçla kullanılabilecek iki türe işaret ediyor: float ve double.

float ve double türlü değerlerin bellek gösterimi, gerçel sayıların temsili için tanımlanan ve pratikte tüm donanımlar (ve programlama dilleri) tarafından benimsenmiş olan IEEE754 standardına göre oluşturulur. Önerilen gösterimin ayrıntısına girmeden önce, anılan türlerin kullanımına dair şu uyarı yerinde olacaktır: tanım aralığındaki tüm değerlerin kusursuz bir biçimde temsil edildiği tamsayı türlerinin aksine, kayan noktalı sayı türleri tanım aralıklarındaki gerçel sayıların sadece bazılarını kusursuz olarak temsil edebilir, diğer sayılar ancak yaklaşık olarak temsil edilebilirler. Bu, tanım aralığındaki gerçel sayılar ile kayan noktalı sayılar arasında bire-bir bir ilişki olmadığı anlamına gelir. Bir diğer deyişle, aynı kayan noktalı sayı birden çok—aslında, sonsuz—gerçel sayıyı temsil eder. Dolayısıyla, aşağıdaki kod parçasının üreteceği çıktı bizi şaşırtmamalıdır. 3.14'ün yaklaşık temsil edilmesi nedeniyle 9.8596 olması gereken çarpma sonucu 9.859601 olarak hesaplanacak ve son satırdaki eşitlik denetimi beklenmedik bir yanıt üretecektir.1
float pi = 3.14f;
float piKare = 9.8596f;
System.out.println(pi * pi == piKare); // ⇒ false
Değişkenlerin ilklenmesinde kullanılan ilk değerlerin sonundaki fF de olabilir—sabitlerin float türünden ele alınması gerektiğini ifade etmek için kullanılır. Bu niteleyicinin yokluğunda, sabitin türü double olarak hesaba katılacaktır.2

Kayan noktalı tanımlayıcılara değer sağlamakta kullanılan sabitler değişik şekillerde yazılabilir.
double bütçeAçığı = 8000000000000000;
bütçeAçığı = 8e15; // 8.0e15 olarak da yazılabilir
bütçeAçığı = 80e14; // Yukarıdakiler ile aynı.
final float üç = 3.f; // 3.0f yazmak daha iyi bir fikir.

double dSayı = 0x49.0p0; // dSayı ← 73 (2^0(4*16^1+9*16^0))
dSayı = 0x4.9p4; // dSayı ↞ 73 (2^4(4*16^0+9*16^-1))
dSayı = 0x1.24p6; // dSayı ↞ 73 (2^6(1*16^0+2*16^-1+4*16^-2))
dSayı = 0xA.5p3; // dSayı ↞ 82.5 (2^3(10*16^0+5*16^-1))
Yukarıda verilen seçeneklerin kullanımında şu noktaların akılda tutulması yararlı olacaktır.
  • Sayının kesir kısmının var olması durumunda, 16'lı taban sadece bilimsel gösterimle birlikte kullanılabilir. Buna göre, Java derleyicisi 0x49 ve 0x49.0p0 seçeneklerini kabul ederken 0x49.0'ı reddedecektir.
  • Bilimsel gösterim sayılarda ölçekleme yapılırken, çarpan olarak 16'lı tabanda 2, 10'lu tabanda ise 10 kullanılır.

İlk kod parçasındaki eşitlik denetiminde ortaya çıkan beklenmedik durumun nedenini daha iyi anlayabilmek için IEEE754'ün koyduğu kurallara bakalım. Öncelikle, gerçel sayıların temsilinde kullanılan kayan noktalı değerlerin içeriklerinin yorumlanmasında ortaokul yıllarından hatırlayacağınız (normalize edilmiş) bilimsel gösterimin temel alındığını söyleyerek başlayalım: kayan noktalı değerlerin içerikleri ∓i0.i-1i-2...x2n şeklinde yorumlanır. Dolayısıyla, normalize edilmiş bilimsel gösterimde i0'ın 0 olamayacağı düşünüldüğünde, kayan noktalı sayı bulunduran bellek bölgelerinin aşağıdaki gibi hesaplanan bir sayıyı tuttuğunu söyleyebiliriz.

∓1.i-1i-2...x2n = ∓(1 + i-12-1 + i-22-2 + ...)2n

Bir diğer deyişle, gerçel sayılar işaret bilgisi, kesir kısmındaki ikili basamaklar ve ölçeklemek amacıyla kullanılan üs değeri kullanılarak temsil edilirler. İşaret bilgisinin bir ikil ile gösterildiği float ve double türleri arasındaki fark, diğer iki özellik için ayrılan alanın büyüklüğünden kaynaklanır. Bu alanların büyüklüğü aşağıdaki tabloda verilmiştir.

Kayan noktalı sayı türleri ve özellikleri
Özellikdoublefloat
Uzunluk (ikil)İşaret11
Üs118
Kesir5223
Duyarlık (Ondalık)167
Merkez1023127

Temsil edilebilecek gerçel sayılara dair yoruma girişmeden önce, tablodaki özelliklerin sayının değerini nasıl etkilediğine bir bakalım.
  • İşaret ikilinin 0 olması sayının artı, 1 olması ise eksi olduğunu gösterir. Bu ikilin 1'e tümlenmesi—yani, 1 iken 0 veya 0 iken 1 yapılması—temsil edilen sayıyı -1 ile çarpma etkisini yaratır. Bunun doğal bir sonucu olarak, artı ve eksi sıfırdan bahsedilmesi de mümkündür.
  • Kesir kısmının en küçük değeri—tüm ikillerin 0 olması durumu—0 olabilirken en büyük değeri—tüm ikillerin 1 olması durumu—1-2-kesir uzunluğu olabilir. Bu değerlerin, başta varsayılan 1 değerine eklenmesi mantis değerini [1..2-2-kesir uzunluğu] aralığına taşıyacaktır. Ayrıca, 52 ve 23 ikili basamak duyarlığında temsil edilen sayılarımızın ondalık basamak türünden en yüksek duyarlığı, sırasıyla, 16 ve 7 olacaktır.3
  • [1..2-2-kesir uzunluğu] aralığındaki mantisin eksi olmayan üs değerleriyle ölçeklenmesi sonucunda mutlak değerce 1'den küçük sayıların temsil edilemeyecek olması nedeniyle, üs kısmı tablodaki Merkez adlı maddedeki değerin sıfır noktası seçildiği bir tamsayı çizgisine eşlenir. Örneğin, float türlü bir sayının üs kısmının 130 olması, ölçeklemekte kullanılan üs değerinin 3 (130-127) olarak alınmasına neden olacaktır. Ayrıca, üs kısmının 1'lerle dolu gösterimi ∓∞ ve temsil edilemeyecek mutlak değerce büyük sayıları göstermek için kullanıldığından, en büyük üs değeri 2üs uzunluğu-1-merkez olacaktır. Benzer şekilde, üs kısmının 0'larla dolu gösterimi ∓0 ve temsil edilemeyecek mutlak değerce küçük sayıları göstermekte kullanıldığından, en küçük üs değeri de 1-merkez olacaktır.


Buna göre, double ve float türlerince temsil edilebilecek en büyük değer, sırasıyla, (2-2-52)22046-1023 ve (2-2-23)2254-127 olarak hesaplanacaktır.4 Temsil edilebilecek mutlak değerce en küçük sayılar ise, her iki tür için de 21-merkez formülü kullanarak bulunabilir. Bu değerler ve Java programları içinden bu değerlere atıfta bulunmak için kullanılabilecek simgesel sabit adları aşağıdaki tabloda verilmiştir.

Kayan noktalı sayıların değer sınırları
Sınır türüdoublefloat
En büyük21024-2971Double.MAX_VALUE2128-2104Float.MAX_VALUE
En küçük2-10222-126
En küçük (özel)2-1075Double.MIN_VALUE2-150Float.MIN_VALUE

Yukarıdaki tablonun son sırasında verilen değerler, üs kısmının tümüyle 0 olduğu gösterimde kullanılan özel hesaplamanın üreteceği değerlerdir. Bu özel gösterime sahip bir kayan noktalı sayının temsil ettiği değerin hesaplanması esnasında kesir kısmına 1 eklenmez ve sonuç kesir kısmı ile 2-merkez'in çarpılması yoluyla elde edilir. Bu özel uygulama sayesinde, mutlak değerce daha küçük sayıların temsil edilmesi olanaklı kılınır.

Verilen sınırlar göz önünde bulundurularak, kayan noktalı sayı türlerinin hangi aralıktaki gerçel sayıları temsil edebileceği aşağıdaki şekille özetlenebilir. Şeklin incelenmesi sırasında temsil edilebilir aralıktaki sayıların bazıları dışında tümünün ancak yaklaşık olarak temsil edildiği unutulmamalıdır.


Kısaca açmak gerekirse;
  • ➃ 0 noktasını, ➁ ve ➅ nolu bölgeler ise 0 dışındaki temsil edilebilir gerçel sayı aralıklarını gösterir. ➁ nolu bölgenin sınırları, double türü için [-Double.MAX_VALUE..-Double.MIN_VALUE], float türü için [-Float.MAX_VALUE..-Float.MIN_VALUE] olarak belirlenirken, ➅ nolu bölgenin sınırları, sırasıyla, [Double.MIN_VALUE..Double.MAX_VALUE] ve [Float.MIN_VALUE..Float.MAX_VALUE] olarak belirlenmiştir.
  • ➀ ve ➆ nolu bölgeler mutlak değerce temsil edilemeyecek büyük gerçel sayıları, ➂ ve ➄ nolu bölgeler ise mutlak değerce temsil edilemeyecek küçük gerçel sayıları kapsar. ➀ ve ➆ nolu bölgelerdeki gerçel sayılar, sırasıyla, -∞ (Double.NEGATIVE_INFINITY veya Float.NEGATIVE_INFINITY) ve +∞ (Double.POSITIVE_INFINITY veya Float.POSITIVE_INFINITY) olarak ele alınırken, ➂ ve ➄ nolu bölgelere düşen gerçel sayılar, sırasıyla, -0 ve +0 olarak ele alınır.
Açıklık getirmek için aşağıdaki kod parçasını ele alalım. f1 değişkenini float tanımlayıcıların alabileceği en yüksek değerin 1 fazlasıyla ilklememiz sonrasında, ikinci satırda aynı değişkeni Float.MAX_VALUE ile karşılaştırıyoruz. Normalde yanlış olması gereken eşitlik denetiminin sonucu, söz konusu gerçel sayıların aynı kayan noktalı sayı ile temsil edilmesi nedeniyle, doğru sonucunu üretiyor. Sonraki iki satırda, en büyük float değer olarak hesaba katılan d1'in tersini bir kez daha d1 ile bölüyoruz. Sonuç, temsil edilemeyecek kadar küçük bir sayı olduğu için, 0.0 (ve -0.0) olarak ele alınıyor. Benzer bir durum, 5. ve 6. satırlar için de geçerli; bu sefer, işlemin sonucu temsil edilemeyecek kadar büyük olduğu için, f3 ve f4, sırasıyla, ∞ ve -∞ ile ilkleniyor. Kod parçasının son satırında ise, ∞'un -∞ ile bölünmesi, f5 değişkeninin "Sayı Değil" (İng., Not A Number) özel değeriyle ilklenmesi sonucunu doğuruyor.
float f1 = Float.MAX_VALUE + 1; // f1 ← 3.4028235E38
System.out.println(f1 == Float.MAX_VALUE); // ⇒ true
float f2 = 1 / f1 / f1; // f2 ← 0.0
f2 = -1 / f1 / f1; // f2 ↞ -0.0
float f3 = f1 * f1; // f3 ← Float.POSITIVE_INFINITY
float f4 = -f1 * f1; // f4 ← Float.NEGATIVE_INFINITY
float f5 = f3 / f4; // f5 ← Float.NaN

  1. Bazılarınız, böylesine can sıkıcı bir durumun varlığını IEEE754 komitesinin yaptığı bir gafa bağlayabilir. Ancak, bilgisayarların sonlu makine olma özelliği ve herhangi bir gerçel sayı çifti arasında sonsuz sayıda gerçel sayı olması birlikte düşünüldüğünde, bunun çok haksız bir yakıştırma olduğu kolaylıkla görülecektir.
  2. Aslına bakarsanız, f ekinin konmaması örneğimizde bir hataya neden olmayacaktır. Ancak, ilkleme sonrasında değişkenlerimize benzer bir şekilde niteleyicisiz bir şekilde kayan noktalı sayı sabitlerinin atanmaya çalışılması derleyici tarafından reddedilecektir.
  3. Duyarlığın hesaplanmasında yapılması gereken, ikili basamak sayısını log102 değeriyle çarpmak ve çıkan sonucu bir üst tamsayıya yuvarlamak.
  4. 1 + 1/2 + 1/4 + ... 1/2-n ifadesinin 2-2-n'e eşit olduğunu hatırlamanız, hesaplamanın doğruluğuna ikna olmanızı kolaylaştıracaktır.

30 Mart 2011 Çarşamba

İlkel Türler-Karakterler

İlkel türlere dair ikinci yazımızda metinsel öğelerin atomik parçası olarak düşünülebilecek simgelerin Java karşılığı olan karakter türüne—char'a—değineceğiz. Anlatımımıza, tamsayı türlerinden bahsederken üzerinde durmadığımız bir noktayı vurgulayarak başlayalım: ne tür bir değer temsil ediyorsak edelim, eninde sonunda her şeyin işlemci tarafından yorumlanabilen ikil (İng., bit) desenleri haline dönüştürülmesi gerekir. Örneğin, geçen yazımızın konusu olan tamsayı türleri 2'nin tümleyeni adı verilen bir temsil yöntemi kullanılarak ikillere dönüştürülür. Karakter tablosu olarak adlandırılan bir küme içinden seçilebilen karakterler—yani, metni oluşturan simgeler—için de benzer bir dönüşüm söz konusudur. Bu dönüşüm, karakter tablosundaki karakterler ile ikil desenleri arasındaki eşlemeyi tanımlayan bir karakter kodlaması yoluyla yapılır.

Değişik ortamlardaki karakterler için farklı kodlamaların kullanıldığı Java'da, Unicode tablosundan seçilen karakterler bellekte UTF-16 adı verilen bir kodlamaya göre temsil edilir. Bir diğer deyişle, karakterleri tutmakta kullanılan iki sekizli büyüklüğündeki char türüne ait bir tanımlayıcı, kendisine sağlanan Unicode karakterin UTF-16 kodlamasını tutar. Buna göre, aşağıdaki satırların işlenmesi sonrasında tanımlanan simgesel sabitler, sırasıyla, ilklemede kullanılan karaktere karşılık gelen 97, 305 ve 351 değerlerine sahip olacaktır.
final char aHarfi = 'a'; // aHarfi <- 97
final char ıHarfi = 'ı'; // ıHarfi <- 305
final char şHarfi = 'ş'; // şHarfi <- 351
Karakterlerin temsilinde kullanılan değerin eksi olmayan bir tamsayı olması, karakter sabitlerin ve karakter tanımlayıcıların tamsayı türüne sahipmiş gibi kullanılabileceği anlamına gelir. Ancak, anlaşılması ve bakımı zor kodlara neden olacağı için, bu özelliğin kullanımından olabildiğince kaçınılmalıdır. Ayrıca, tamsayı türlerinin 0-merkezli bir sayı çizgisindeki eksi ve artı sayıları temsil edebilirken, char türünün eksi olmayan tamsayıları temsil edebileceği unutulmamalıdır.
int sıraNo = 'a'; // sıraNo <- 97
sıraNo++;
char harf = (char) sıraNo; // harf <- 'b'
harf = (char) 67; // harf <- 'C'
char türlü bir tanımlayıcıya tamsayı değer sağlanmasının bir getirisi, klavyenizde bulunmayan Unicode karakterlerin girilmesine olanak tanımasıdır. Örneğin, matematiksel ifadelerin işlendiği bir metnin parçası olarak kullanmak isteyebileceğiniz sigma (Σ), klavyenizde bulunmadığı halde karşılığı olan 0x3A3—931 veya 03243 de olabilir—değerinin yazılmasıyla girilebilir.
final char sigma = '\u03A3'; // sigma <- 'Σ'
Dikkat edecek olursanız, yukarıda sağlanan örnek bir öncekinden farklı bir gösterim kullanıyor. \u önekinin derleyiciye verdiği ön bilgi sayesinde, bu gösterimin kullanıldığı durumlarda biçimlendirme yapmak gerekmiyor; derleyici \u öneki sonrasındaki sayıyı ilişkin karakterin Unicode tablosundaki konumunun 16'lı tabandaki dört basamaklı hali olarak ele alıyor ve bekleneni yapıyor.

Tamsayı değerlerin char türlü olarak ele alınabilmesinin bir diğer yararı, varlığı metnin görünümünde dolaylı bir biçimde etki yaratan kontrol karakterlerinin temsil edilmesinde ortaya çıkar. Örnek olarak, bir sonraki satırın başına geçiren yeni satır karakterini ele alalım. Bu karakter, metni okuyacak düzenleyici benzeri bir yazılım tarafından okunmalı ve hesaba katılmalı, fakat bu karakterin varlığı metni okuyan kişilere doğrudan yansıtılmamalıdır. Bundan dolayı, Unicode tablosunda 10. sırada yer alan bu karakter '\u000A' değeri ile temsil edilir. Sıklıkla kullanılan bu karakterin daha kolay yazımını sağlamak amacıyla algılaması daha kolay ikinci bir gösterim kullanılabilir: '\n'. Bu ve bazı diğer kontrol karakterleri aşağıdaki tabloda verilmiştir.

Özel gösterimli karakterler
Karakter adıGösterimKarakter
Geri alma'\b''\u0008'
Sayfa ilerletme'\f''\u000C'
Yeni satır'\n''\u000A'
Satır başı'\r''\u000D'
Sektirme'\t''\u0009'
Tek tırnak'\'''\u0027'
Tırnak'\"''\u0022'
Ters kesme'\\''\u005C'

Tablonun son üç sırasındaki karakterlerin, diğerlerinden farklı olarak, görsel gösterimli karakterler olduğunu fark etmişsinizdir. Kontrol karakteri sınıfına girmeyen bu karakterlerin bu şekilde kullanılması, söz konusu karakterlere Java'da yüklenen özel anlamdan kaynaklanır. Örneğin, karakter sabitlerinin başlangıç ve sonunu işaretlemek için kullanılan tek tırnak iminin bu görevi dışında kullanılabilmesi için özel ele alınması isteğimizin derleyiciye iletilmesi gerekir. Bu da, yukarıdaki tabloda belirtildiği gibi yapılacaktır.

Yazımızı, çok ufak bir olasılıkla da olsa, bazılarınızın canını yakabilecek bir ayrıntıya değinerek kapatalım: Başlangıcında iki sekizliye sığan indislerle kullanılabilen Unicode tablosu, zaman içinde yeni karakterlerin eklenmesiyle genişlemiş ve indis değerleri iki sekizliye sığmaz hale gelmiştir. Bu da, Unicode tablosundaki kimi karakterlerin iki sekizli yer tutan char türündeki bir tanımlayıcı ile temsil edilememesi durumunu doğurmuştur. Yeryüzünde konuşulmakta olan modern dillerin simgeleri için geçerli olmayan bu durumun, nadiren de olsa sürprizler yaratabileceği unutulmamalıdır. Örneğin, müzik gösteriminde yararlanılan simgelerin kullanımı unutkan bir programcıya bu durumu hatırlatacaktır. Zira, bu tablodan da görüleceği gibi, müziksel simgeler Unicode tablosunun 0x1D100 ile 0x1DFF nolu indisleri arasında bulunur ve ancak dört sekizli ile temsil edilebilir. İşin kötüsü, dört sekizli kaplayan bu alana ilişkin karakterin Unicode tablosundaki indis değeri değil UTF-16 kodlamasının ürettiği bambaşka bir değer konulması gerekecektir. Bu değerin ne olacağını merak edenlere UTF-16 kodlaması konusundaki şu Sıklıkla Sorulan Sorular belgesini okumalarını tavsiye ederim.

16 Mart 2011 Çarşamba

İlkel Türler-Tamsayılar

Bilgisayar programları, gerçek dünyada ve zihinlerimizde hali hazırda var olan süreçleri benzetmek (simüle etmek) amacıyla yazılır. Bu, kimi zaman verilen bir sayının çift olup olmadığını bulan bir hesaplama kadar basit olabilirken, kimi zamansa Mars gezegenine gönderilen bir aracın (Mars Rover) gezegen yüzeyinde güvenli bir biçimde dolaşmasını sağlayan yazılım kadar karmaşık olabilir. Ancak, üretilmesi istenen yazılım ne kadar karmaşık olursa olsun, iş eninde sonunda benzetilen süreçteki varlıkların özelliklerini temsil eden—ve genelde bilgisayar donanımları tarafından da bilinip makine kodları yoluyla doğrudan işlenebilen—basit türden değerlerin işlenmesi yoluyla icra edilir.  Örneğin, öğrenci kayıtlarını işleyen bir yazılımda gerçek iş, öğrencinin no, ad, ortalama gibi basit değerli özelliklerinin işlenmesiyle yapılır. İşte bu yazımızda, bilgisayar donanımı tarafından doğrudan desteklenmesi nedeniyle ilkel veri türü (kısaca, ilkel tür) olarak sınıflandırılan bu basit değerlerin ait oldukları türlerin tamsayıları temsil etmekte kullanılanlarına bakacağız.

Öncelikle, son tümcenin programlamaya yeni başlayanlarınızı şaşırtabilecek çoğul olma özelliğine dikkatinizi çekerim. Matematikte ℤ = {..., -2, -1, 0, 1, 2, ...} şeklinde tanımlanan ve sayma sayıları olarak adlandırılan tek bir tür varken, Java'da belirli sınırlar içindeki tamsayıları temsil eden dört türden bahsedebiliriz: byte, short, int ve long.1 Söz konusu türlerin belirli sınırlar içinde olmasının sebebi bilgisayarın sonlu bir belleğe sahip olmasında yatar: türleri ne kadar büyütmek istersek isteyelim, bilgisayar belleği pratik bir sınır olarak karşımıza çıkacaktır. Dört değişik tamsayı türünün bulunmasının nedeni ise, ortaya çıkan farklı gereksinimler sonucunda belleği ekonomik kullanma seçeneğini programcıya sunmak isteğidir.

Java tarafından desteklenen tamsayı türlerinin bellekte kapladığı alan ve bu türlere sahip terimlerin alabileceği değerlerin alt ve üst sınırları aşağıda verilmiştir. Buna göre, öğrenci notlarını tutmakta kullanılacak bir tamsayı dizisinin elemanlarının int yerine byte seçilmesi yerden %75'lik bir tasarruf anlamına gelecektir.

Tamsayı türleri ve sınır değerler2
Tür adıAlan (ikil)En küçükEn büyük
byte8-128127
short16-3276832767
int32-21474836482147483647
long64-9223372036854775808L9223372036854775807L

Tamsayı türlü ifadelerin işlenmesi sırasında, sınır dışına taşabilecek işlem sonuçlarının sessizce göz ardı edildiği ve elde edilen sonuçların aşağıda int türü için verilmiş olan öncelik-sonralık ilişkisine göre dönüştürüleceği unutulmamalıdır. [Integer.MAX_VALUE, herhangi bir int türlü ifadenin alabileceği en büyük değeri gösterirken, Integer.MIN_VALUE aynı ifadenin sahip olabileceği en küçük değeri temsil eder. Diğer türler için benzer işlev gören simgesel sabitlerin adları, Integer yerine Byte, Short veya Long konularak elde edilebilir.]


Dolayısıyla, aşağıdaki program s: -32767 b: 124 şeklinde bir çıktı üretecektir. Çünkü, s'nin yeni değeri 32767'den iki sonra gelen 32769 değil, yukarıdaki şeklin short türüne adapte edilmiş uyarlamasının gerektirdiği gibi -32767 olacaktır. Benzer şekilde, -127 değerine sahip b değişkeninden 5 çıkarılması sonrasında bu değişkenin yeni değeri 124 olacaktır, -132 değil.

TamsayıSınırları.java
public class TamsayıSınırları {
  public static void main(String[] ksa) {
    short s = 0x7ffF; // short s = 32767;
    s = (short) (s + 2);
    byte b = -0177; // byte b = -127;
    b = (byte) (b - 5);
    System.out.println("s: " + s + " b: " + b);
  } // void main(String[]) sonu
} // TamsayıSınırları sınıfının sonu

Örnek programımızın 4. ve 6. satırlarındaki biçimlendirmeler dikkatinizi çekmiştir. Zorunlu olarak yapılan bu işlemlerin sebebi, 2 ve 5 gibi tamsayı sabitlerinin değerleri ne kadar küçük olursa olsun int türlü kabul edilmeleridir. Bunun bir sonucu olarak, söz konusu satırların  biçimlendirmeler olmaksızın yazılması, bir short/byte ile bir int değerin toplanması/çıkarılması işleminin short/byte türü sınırları dışında olması olasılığı nedeniyle derleyici tarafından reddedilecektir. Çünkü, elde edilecek int türündeki değerin atama sonucunda daha küçük türden bir değişkenin yeni değeri olması olası bir değer kaybına işaret eder. Derleyici engeli, programcının bilinçli olarak bu işleme izin verdiğini belirten biçimlendirme ile aşılabilir.

Program metninin 3. ve 5. satırlarından da görülebileceği gibi, tamsayı sabitleri ondalık sistem dışında bir gösterim ile de yazılabilir; istenecek olursa, sayıların yazımında 8'li ve 16'lı taban da kullanılabilir.3 Bu takdirde, 0 ve 0x öneki ile başlayan sayıların yazımında kullanılabilecek simgelerin, sırasıyla, [0..7] ve [0..9, A..F, a..f] içinden seçilebileceği unutulmamalıdır.

Değineceğimiz son nokta, int sınırları dışındaki bir tamsayı sabitinin program metnine yazımı için kullanılması gereken gösterim. Küçük mutlak değerli tamsayılara da uygulanabilecek bu gösterim sayesinde, herhangi bir sabitin long türlü ele alınması sağlanabilir. Yapılması gereken tek şey, söz konusu sabitin sonuna L (veya l) eklemektir. Dikkat edildiğinde, aynı işin biçimlendirme ile, aşağıdaki kod parçasının ikinci satırında olduğu gibi, yapılmasının olanaksızlığı görülür. Zira, biçimlendirilme işlemi tamsayı sabitinin öncelikle int büyüklüğündeki bir bellek bölgesine konulmasını gerektirir ki, bu biçimlendirme öncesinde değer kaybının zaten yaşanmış olacağı anlamını taşır.
long l = 123456789098765L;
long l2 = (long) 123456789098765; // Derleme hatası!

  1. Karakterler bahsinde, char türünün de tamsayıları temsil etmek için kullanılabileceğini göreceğiz.
  2. Java 7 ile birlikte, sayıların algılanmasını kolaylaştırmak amacıyla alt çizgi (_)  kullanılarak rakamların gruplanması mümkün olacaktır. Örneğin, 123456789 yerine 123_456_789 yazılabilecektir.
  3. Java 7 ile birlikte, tamsayı sabitlerinin 0b öneki belirtilerek 2'li tabanda yazılması mümkün olacaktır.