C# Kayar Nokta Veri Türleri
Kayar Nokta Veri
Türleri
Bilimsel ve mühendislik uygulamalarda, genellikle çok küçük ve çok
büyük sayılarla çalışmak ve daha fazla esnek olmak istersiniz. C#taki
decimal veri türü sadece ondalık kısmın sağında ve solunda 28
basamağa kadar izin verir. Bu sınırlamalardan dolayı esnek değildir.
Mesela güneşin ağırlığı yaklaşık 2*1030 kilogramdır. Elektronun
ağırlığı ise yaklaşık 9*10-31 kilogramdır.
Uzun yıllar, bilim adamları 3 ya da 4 hassasiyetli sayılarla
işlemlerini kaydırma kuralları ile yaptılar. Günümüzde, kayar nokta
veri türlerini kullanarak bilimsel gösterime yakın bir biçimde
sayıları gösterip bilimsel hesaplamalarını yapıyorlar. C#ta 2 tane
kayar nokta türü vardır, float ve double.
1985 yılında IEEE kayar nokta türleri ile ilgili standartları
belirledi. İki tane tür belirlendi. 4 byte gerektirene tek
hassasiyet 8 byte gerektirene de çift hassasiyet denildi.
.net Framework içerisinde, bu iki kavramı karşılayan System.Single
ve System.Double adında iki tane yapı vardır. Fakat C# double ve
float isimlerini C dinliden miras aldı. Aşağıdaki tablo double ve
float hakkında bilinmesi gerekenleri özetlemektedir.
C# Türü
.NET Türü
Anlamlı Kısım
Üssel Kısım
float
System.Single
23 bits (7 basamak)
8 bits (-45 to 38)
double
System.Double
52 bits (1516 basamak)
11 bits (-324 to 308)
Sağ taraftaki iki sütun float türünün 32 bitinin ve double türünün
64 bitinin, anlamlı kısımla üssel kısım arasında nasıl
paylaşıldığını göstermektedir. Ek bir bit ise anlamlı kısmın
işaretini tutar. Aşağıdaki sayıyı inceleyelim.
8.364 10-24
Anlamlı kısmı 8.364 olan kısmıdır. Float veri türünde 23 bit anlamlı
kısım için ayrıldığına göre bu kısım yaklaşık 7 basamağa kadar
olabilir. Üssel kısım için ise 8 bit ayrılmıştır.
float ve double veri türlerini gösterirken Macar gösterimi
kullanalım. float için f önekini, double için de d önekini
kullanalım.
float f;
Tabi ki virgülden sonraki kısmı olmayan bir sayıyı yani bir
tamsayıyı da float bir değişkende tutmak mümkündür.
f = 123456789;
Tamsayının kayar nokta türüne dönüşümü sırasında tamsayı hassasiyet
kaybına uğrar. Eğer bu sayıyı ekranda görmek istersek aşağıdaki kodu
yazıp çıktısını görelim.
float f;
f = 123456789;
Console.WriteLine(f);
Console.ReadLine();
Aslında aynı sayıdır. Sayının genliği değişmez ama 7 basamaktan
yukarısını float ile gösteremediğimiz için hassasiyet kaybı olur.
float bir değişkene ondalık kısmı olan bir sayı ataması yapılabilir.
f = 45.384f;
Bilimsel gösterimde bir sayıyı da float bir değişkene atayabiliriz.
f = -34.78e-14f;
Yukarıdaki örneklerde de görüldüğü gibi eğer tamsayı değil de
ondalık kısmı olan ya da bilimsel gösterimdeki bir sayıyı float bir
değişkende tutmak istersek sayının sonuna küçük harfle ya da büyük
harfle F yazmak gerekiyor. Ayrıca bilimsel gösterimdeki e harfini de
büyük ya da küçük yazmak fark etmez.
Şimdi de bir double değişken tanımlayalım.
double d = 456.374584;
Double bir sayı tanımlarken herhangi bir sonek koymaya gerek yok ama
gene de küçük ya da büyük harfle d harfi koyulabilir. Diğer bir
ifadeyle ondalık kısmı olan ya da bilimsel gösterime sahip olan
sayılar varsayılan olarak double kabul edilir.
Bir çok programcı sadece double kullanır ve float ile ilgilenmez.
Çünkü kayar nokta işlemleri işlemcinin matematik işlemler bölümünde
özel bir kısımda ele alınır, bu yüzden double yerine float
kullanmanın performans üzerinde bir etkisi yoktur. Günümüz
uygulamalarında çoğu zaman belleğin verimli kullanımı ihmal
edilebilir.
Gerekli durumlarda C# tamsayı türünden bir sayıyı kapalı olarak
float ya da double türüne dönüştürebilir. Biraz hassasiyet kaybı
olur ama genliğinde bir kayıp söz konusu olmaz. Hatta C# float
türünü de aynı şekilde double türüne kapalı olarak dönüştürebilir.
C# kapalı olarak herhangi bir tamsayı türünü float ya da double
türüne dönüştürmez. Eğer dönüştürme yapacaksak casting yapmak
gerekiyor. Hangi türün hangi türe kapalı olarak dönüştürülebileceği
aşağıdaki şekilde görülmektedir.
IEEE kayar nokta türleri (float, double) için standart tanımlarken
diğer sayısal veri türlerine göre çok farklı tanımlamıştır. Mesela
kayar nokta hesapları yapılırken asla istisna oluşmaz. Aşağıda tam
sayılar için sıfıra bölme istisnası oluşturabilecek bir örnek var.
double dPay = 124;
double dPayda = 0;
double dBolum = dPay / dPayda;
Console.WriteLine(dBolum);
Console.ReadLine();
İşlemin sonucunda dBolum adlı değişkene çok özel bir değer dönüyor.
Bu özel değerler IEEE tarafından belirlenmiştir. Infinity (sonsuz)
değeri de bunlardan biridir.
Eğer dPay değişkenini -124 diye değiştirirsek o zaman da Infinity
(- Sonsuz) sonucunu göreceksiniz.
Eğer dPay değerini de 0 olarak değiştirirseniz NaN göreceksiniz. NaN
(Not a Number Sayı Değil) anlamına gelmektedir. Eğer kayar nokta
hesabında aşağı taşma ya da yukarı taşma durumu meydana gelirse o
zaman da NaN değeri görüntülenecektir.
float ve double yapıları PositiveInfinity, NegativeInfinity, ve NaN
olan üç adet alana sahiptir. Bu alanlar açık olarak değer ataması
yapabilmek içindir. Mesela, eğer double tanımlanmış 1 sayısını
Positive Infinity değerine bölerseniz 0 sonucunu alırsınız ama
PositiveInfinity değerini PositiveInfinity değerine bölerseniz 1
yerine NaN sonucunu alırsınız.
Başka bir yazıda bu özel değerlerle nasıl çalışılacağını
anlatacağız. Şimdilik kayar nokta veri türleri ile çalışırken bu tür
sonuçların görülebileceğini bilsek yeter.
Kayar nokta türlerini, çok büyük ya da çok küçük sayıların bölme ve
çarpma gibi işlemlerini yaparken doğal olarak tercih etmeniz
gerekiyor. Çünkü bu tür bir işlemde hiçbir zaman taşma meydana
gelmeyecektir.
Eğer C# kullanarak bilimsel ya da mühendislik hesaplamalar yapmak
isterseniz üssel, logaritmik ve trigonometrik fonksiyonlarla
hesaplamalar yapmak isteyeceksiniz. Bütün bunlar System isim
uzayındaki Math sınıfının içerisinde statik yöntemler olarak
mevcuttur.
Meselâ, üs alma işlemini Pow yöntemini kullanarak yapabilirsiniz.
dSonuc = Math.Pow(dTaban, dUs);
Bu yönteme virgülle ayrılmış 2 tane argüman geçeriz. double olmayan
argümanlar kapalı olarak double türüne çevrilir.
double dSonuc = Math.Pow(3, 4);
Console.WriteLine(dSonuc);
Console.ReadLine();
Ama decimal veri türü hiçbir türe kapalı olarak çevrilemeyeceğinden
ötürü decimal sayıları bu yönteme geçmek için açık dönüşüm yapmak
gerekiyor.
Karekök almak için Pow yöntemini ilgili sayının 0.5 kuvvetini almak
suretiyle kullanabilirsiniz.
dSonuc = Math.Pow(dSayi, 0.5);
Ya da karekök almak için olan Sqrt yöntemini kullanabilirsiniz.
dSonuc = Math.Sqrt(dSayi);
Şimdi bir dik üçgenin hipotenüsünü hesaplayabileceğimiz küçük bir
örnek yazalım. Pisagor teoremine göre dik üçgenin hipotenüs uzunluğu
dik kenarlar uzunluklarının kareleri toplamının kareköküne eşittir.
Math.Sqrt yöntemine argüman olarak iki tane Math.Pow işleminin
sonuçlarının toplamlarını geçeceğiz.
using System;
class Program
{
static void Main()
{
Console.Write("İlk Kenar: ");
double dKenar1 = Double.Parse(Console.ReadLine());
Console.Write("İkinci Kenar: ");
double dKenar2 = Double.Parse(Console.ReadLine());
double dSonuc = Math.Sqrt(Math.Pow(dKenar1, 2) + Math.Pow(dKenar2,
2));
Console.WriteLine("Hipotenüs hesaplandı: {0}", dSonuc);
Console.ReadLine();
}
}
Math sınıfının ayrıca iki tane sabit alanı vardır, matematik
hesaplarda sık kullanılan iki tane sabit değerli sayı.
pi sayısını Math.PI, e sayısını da Math.E alanları bize verir.
Bazı uygulamalarda kayar nokta vazgeçilmezdir ama her yerde değil.
Gereksiz kullanımlarda bir takım sıkıntılar da meydana getirir.
Klasik BASIC programlama dili bütün sayısal verileri kayar nokta
şekilde tutar. Daha sonraki programla dilleri her sayı türü için
ayrı değişken türü tanımlamaya olanak tanısa da programcılar
çoğunlukla kayar nokta türleri tercih ederler. Çünkü alt taşma yok,
üst taşma yok, daha kolay gelir.
Kayar nokta türlerle uzun süre haşır neşir olmuş programcılar
sıkıntılarını da iyi bilirler. Mesela 23.5 gibi bir sayı 23.50001 ya
da 23.49999 olarak saklanmış olabiliyor. İkincisi büyük bir
problemdir. Çünkü en yakın tamsayıya yuvarlamak istersek 23.49999
sayısı 23 sayısına 24 sayısından daha yakındır.
Yeni programlama dillerinde kayar nokta türler çok fazla problem
olmuyor. Sürekli birileri daha sağlıklı hesaplamalar yapmaya izin
verecek çalışmalar yapıyor. Ama hala sıkıntılar var. Aşağıdaki
örneği inceleyelim.
Console.WriteLine(9.666e-7f);
Gördüğünüz gibi WriteLine bize aynı sayıyı göstermedi.
Şimdi de aşağıdaki örneğe bakalım.
using System;
class Program
{
static void Main()
{
float f1 = 1234567;
float f2 = 0.1234567f;
float f3 = f1 + f2;
Console.WriteLine(f3);
Console.ReadLine();
}
}
float veri türü sadece 7 basamak gösterebiliyor, işlemin sonucunu da
görüyorsunuz. f2 değişkeninin değeri kayıp. Eğer 7 basamaktan daha
büyük sayılar gösterecekseniz double ver türünü kullanabilirsiniz.
Ama double veri türünün de 16 basamaktan fazlasını tutamayacağını
bilmeniz gerekiyor.
Çok az denk gelse de IEEE standardı bizim istediğimiz sayıyı tutmaya
yetmiyor. Bu genellikle anlamlı kısmın hassasiyetinden
kaynaklanıyor. 16,777.216 ile 16,777.217 float sayıları arasındaki
fark ihmal ediliyor. Çok küçük ve çok büyük sayılarda yakınsama söz
konusudur. Meselâ, 12.34 sayısı float olarak saklandığı zaman
12,939,428 tamsayısının 220 ile bölümü olarak saklanır. Bu sayı
yaklaşık olarak 12.34 ediyor. Eğer double olarak saklanırsa
6,946,802,425,218,990 tamsayısının 249 ile bölümü olarak saklanır.
Bu sayı 12.34 sayısına çok çok yakındır, ama aynısı değildir.
Aynı sayıyı decimal olarak saklarsak 1234 sayısının 102 ile bölümü
olarak saklanır. Bu sayı tam olarak 12.34 eder. Bu durum decimal
veri türünün ne kadar güçlü olduğunu gösteriyor. Bunun yanında şu
gerçeği de göz önünde bulundurmak gerekiyor, kayar nokta türler
bilimsel ve mühendislik hesaplarda kullanılsın diye var. Bilimsel ve
mühendislik hesaplarda da her zaman yakınsama ve yuvarlama söz
konusudur. Kesinlik gerektiren ama ondalık kısmı olan işlemler için
zaten C# decimal diye bir tür içeriyor ve oldukça güçlü.
|