17 Mart 2014 Pazartesi

IEEE 754

Not 1: İngilizcede kelime anlamı olarak precision ile ölçülen değerlerin birbirine yakınlığı, Accuracy (Kesinlik) ile de istenilen değere yakınlık kast ediliyor.

  • Bir sayının içerdiği tüm rakamlara precision deniliyor. Java'da precision soldaki ilk sıfırdan farklı sayıyı sayarak başlıyor. Örneğin 0.012 sayısı için precision 2'dir. Sayının tam sayı kısmına whole part, küsurat kısmına fractional part veya decimal part deniliyor. ÖrnekVelocity is a real number that can be expressed as a whole number and a decimal (e.g., 3750.2563...miles per hour). Sayının kaç tane küsurat hanesi alabileceği ise scale ile belirleniyor.

Not 2: Kayan noktalarda sayı 0'a yaklaştıkça temsil edilebilen rakamlar daha yoğunlaşırken (dense), 0'dan uzaklaştıkça temsil edilebilen rakamlar seyreliyor. (sparse). Buradan aldığım şekilde de 0'dan uzaklaştıkça olan seyrelme görülebiliyor.

Dolayısıyla float için veri aralığı 3.4E +/- 38  olarak tanılanmasına yani 3400,000,000,000,000,000,000,000,000,000,000,000,000.00 gibi dev bir sayı olmasına rağmen aslında seyrelme yüzünden bazen kullanışsız olabiliyor.

Bu yüzden sum of series using float sorusunda da açıklandığı gibi seri toplama işlemlerinde önce küçük sayıları toplamak hata olasılığını azaltıyor.

Yine aynı şekilde The Patriot Missile Failure yazısında açıklandığı gibi 1/10 gibi bir sayı tam olarak temsil edilemiyor.

Not 3: IEEE-754 Floating-Point Conversion sitesinden online calculator bulunuyor.

Giriş
Kayan nokta hesaplaması için kullanılan iki standart var. İlki "IEEE 754", ikincisi ise "IEEE 854"

Kayan noktalarda unutulmaması gereken bir nokta tüm sayıları temsil etmenin imkanı yoktur. Örneği Why does printf(“%f”,64.3f) output 64.300003? sorusundan aldım.


Double
Double'ın bir diğer ismi ise Double-precision floatin-point format.
http://www.leda-tutorial.org/en/unofficial/ch06s03.html adresinden aldığım şekil aşağıda. Bu veri tipi ile küsurat ayracından sonra yaklaşık 15 hane büyüklüğüne kadar sayıları saklamak mümkün.


Exponent bias

Double ile dikkat edilmesi gereken bir husus exponent alanının biased olması. Yani pozitif/negatif değerler yerine sayısı kaydırma yöntemi kullanılıyor ve kaydırma için kullanılan referans değer de 1023. Örneğin exponent olarak 1 elde etmek istiyorsak 11 bitlik bu alana 1024 yazmak gerekiyor çünkü 1024 - 1023 = 1 .

11 bitlik alana 0 .. +2047 arasında bir değer yazılabilir ki bu da -1022 .. +1023 arasında exponent elde edebilmemize neden olur


Float
Float'ın bir diğer ismi ise Single-precision floatin-point format.
http://phimuemue.com/blog.php?article=151 adresinden aldığım şekil aşağıda. Bu veri tipi ile küsurat ayracından sonra yaklaşık 7 hane büyüklüğüne kadar sayıları saklamak mümkün.
Bir başka Türkçe şekli ise buradan aldım.

Float 32 bitlik yer kullanırken double 64 bitlik (iki katı) yer kullanıyor. Bu yüzden ismi double olarak geçiyor.

Float ve Mantissa
Float veri tipinin mantissa alanı sadece 23 bit olduğu için 2^23'ten büyük (8,388,608) yani 8 milyondan biraz büyük tam sayıları float'a atamak hataya sebep oluyor. Mümkünse float yerine her zaman double kullanılmalı.

Buradaki açıklamada da mantissa'dan daha büyük bir int değer, float'a atandıktan sonra karşılaşılan bir hata açıklanmış
 
Exponent bias

Float ile dikkat edilmesi gereken bir husus exponent alanının biased olması. Yani pozitif/negatif değerler yerine sayısı kaydırma yöntemi kullanılıyor ve kaydırma için kullanılan referans değer de 127. Örneğin exponent olarak 1 elde etmek istiyorsak 7 bitlik bu alana 128yazmak gerekiyor çünkü 128- 127 = 1 .
conversion from binary to base 10 using floating point conversion sorusunda bitlerden kesirli sayının nasıl elde edileceğini gösteren örnekler mevcut.

Denormal Number
Denormal sayılar 0 ile en küçük kayan nokta sayısı arasında kalan sayılardır. Şekilde denormal sayılar görülebilir. 
Why does changing 0.1f to 0 slow down performance by 10x? sorusuna verilen cevapta bazı sayıların Denormal number olduklarından bahsediliyor. Denormal sayıların 0.xyz şeklinde temsil edilmeleri gerekiyor ve bu sayılarla işlem yapmak anladığım kadarıyla daha yavaş. Buradaki soruda da bu sayılar ile işlemin daha yavaş olduğu anlatılmış.


Intel 80 bit floating point
80 bitlik precision bazı C++ derleyicileri tarafından kullanılabiliyor. Aşağıdaki cümlede bunu okuyabiliriz.
The C++ standard allows implementations to evaluate floating-point expressions with more precision than the nominal type requires.
FPU içindeki kayan nokta hesaplaması için kullanılan registerlar 80 bit olabiliyor.

C ile kullanılan long double 80 bitlik bu veri tipine denk gelebilir. Bu durumda iki farklı makine arasında aynı hesaplamayı yapsak bile farklı sonuçlar elde edilebilir. Derleyiciler strict mode çalışmaya zorlanarak farklı makineler arasında aynı sonucu elde etmek mümkün. 

C++
/fp:strict - MS derleyici için kullanılır
Platformlar arasında uyumluluğu sağlamak için yukarıdaki gibi 80 bitlik floating point hesaplamaların kullanılmazBuradaki soruya da göz atılabilir. 

Java
strictfp expression kullanılabilir veya  StrictMath kütüphanesi kullanılabilir. Örnek:
public strictfp class MyFPclass { 
// ... contents of class here ...
}


Matematik Kütüphanesi ile Kayan Nokta İşlemleri
C
math.h içindeki bazı fonksiyonlaru buradan görebilirsiniz.
modf (tam sayı ve ondalık sayıya ayırma)
Extract decimal part from a floating point number in C sorusunda da gösterildiği gibi verilen sayıyı modf metodu ile tam sayı ve ondalık sayı kısımlarına ayırmak mümkün. Örnek:
double intpart ;
double fractional = modf(23.345, &intpart);//int part 23,fractional ise 0.345
exp 
e üssü x hesaplamasını yapar. e Euler sayısıdır. Bu metod exp (a) yerine pow (e,a) şeklinde de yazılabilir ancak muhtemelen daha yavaş çalışır.

exp2
2 üssü x hesaplamasını yapar.

fma (floating multiply and add)
Bu fonksisyon tam nerede kullanılıyor bilmiyorum ancak fma (x,y,z) = ((x*y)+z) anlamına geliyor.

frexp (fraction ve exponent'a ayırma)
Bu fonksiyon ile verilen sayıyı significand ve exponent olarak ayırmak mümkün. Metodun döndürdüğü significand/mantissa ya 0 veya [0.5 - 1.0) aralığında oluyor.

Aşağıdaki şekilde bu görülebiliyor. Exponent'in 2 nin üssü şeklinde verildiğini unutmamak lazım. Yani aşağıdaki şekilde exponent olarak 5 verilse bile aslında 2 ^ 5 yani 32 olarak düşünmek lazım. 
0.965625 * 32 = 30.9


Bu yöntemden farklı olarak eğer ondalık sayıyı tam sayı ve küsurat olarak ayırmak istersek aşağıdakine benzer bir kodu da kullanabiliriz.

fmod (bölme sonucunda kalanı bulma)
modulus operatorünün sağ tarafına bir tam sayının gelmesi gerekir. Eğer gelmezse derleyici aşağıdaki gibi bir hata verir.
error: invalid operands of types 'double' and 'double' to binary 'operator%'
Bu durumda fmod metodunu kullamak gerekir. fmod metodunun imzasi aşağıdaki gibi. modulus operatöründen farklı olarak double bir değer döndürüyor.
double fmod (double numer, double denom);

fmod ile bölme sonucunda kalan sayı kolayca bulunabilir. fmod eksi sayı verebilir. Örneğin fmod (-3,4) = -3 verir. Modulus operatörü de eksi bir sayı döndürebilir. Dolayısıyla modulus operatörü ile sayının eksi veya artık olmasına bakmadan tek/çift olduğunu kontrol etmek için aşağıdaki gibi yapılabilir.

Moduler Exponentiation - Right to Left Binary Method
modpow çok büyük sayıların üssünü alma ve daha sonra modülüs operatörü gibi bölümden kalanı bulma yöntemi. Yani pow ve fmod karışımı bir metod. Wikipedia'da ise formül açıklanıyor. Burada ise kod hali var. Tek dikkat edilmesi gereken nokta, exponent yani ikinci parametrenin bir int olması gerektiği kuralı.



mod(bölme sonucunda kalanı artık sayı olarak bulma)
Bu işlem matlab ile geliyor. 

ldexp (2 üssü ile çarpma)
ldexp aslında frexp fonksiyonun tersidir. frexp nasıl verilen floating point sayısı bileşenlerine ayırıyorsa ldexp te bileşenleri tekrar floating point sayıya dönüştürür. Yani verilen sayı ile 2 ^ bir başka sayıyı çarpmayı kolaylaştırır. Şimdiye kadar gördüğüm tek kullanım yeri What is the best way to produce random double on POSIX? başlıklı soruya verilen cevap olmuştur. Başka da hiç bir yerde kullanıldığını görmedim.

Bir diğer muhtemel kullanım alanı da  Writing a floating point multiplication function in C sorusunda açıklandığı gibi eğer kullandığımız platformda floating point çarpma rutini yoksa, kendi çarpma işlemimiz yazmak için olabilir ki böyle bir platform artık kalmışmıdır bilmiyorum.
log (x)
log(x) verilen sayının doğal logaritmasını alır. Doğal logaritmada taban yaklaşık 2,718281828 olan e sayısı (Euler sayısı) dır. Genellikle ln (x) ile ifade edilir. Elimizde sadece log(x) varsa aşağıdaki gibi yapılabilir.
#define log2(x) log(x)/log(2)
yani

log2(x) = ln (x) / ln (2)
ya da
log2(x) = log10(x) / log10(2)
Bölme işlemi çarpmaya göre yavaş olduğu için şu optimizasyon da yapılabilir.
log2(x) = (1 / ln  (2)) * ln (x). 
1 / ln (2) sabit sayı olduğu için önceden hesaplanıyor ve hesaplama sadece çarpmaya dönüşüyor. Bu yöntem, işlemcilerin zayıf olduğu gömülü dünyada kullanılıyor.

log (x,n)
Verilen sayının n tabanında logaritmasını alır.Örnek: Sayının en yüksek (MSB) kaçıncı bitinin 1 olduğunu bulmak. 2^N aslında N'inci bitin atandığı bir sayı yani  1 << N. N değerini bulmak için aşağıdaki yöntem kullanılabilir. 4 gibi bir sayı double olarak 0.4 x 10^1 gibi temsil edilecektir. Exponent'i bulmak için 52 biti kaydırıyoruz. Sonra 11 bit ile AND'leyip normalde bias olan 1023 yerine 1022'den çıkarırsak 1 olarak atanmış MSB bitinin indeksini buluruz.

log10
Logaritma fonksiyonunu alır. Örnek: Sayıyı 10'un katı olacak şekilde üste yuvarlar. Şöyle çalışır Log10 ile sayının sağındaki sıfırları sayar ve üste yuvarlar. Daha sonra pow (10) ile üste yuvarlanmış sayı birleşerek 10'un katı bir sayı bulunur.
Math.pow(10, Math.ceil(Math.log10(x)));  
nextafter
nextafter ile metod ile bir sonraki temsil edilebilen kayan nokta sayıyı bulabilmek mümkün.
Aynı işlemi Java'da Math.nextUp() ile yapıyoruz. Örnek :
double d1 = Math.nextUp(1.345); //çıktı olarak 1.3450000000000002 verir
pow
Sayının üssünü alır. Bu metoda üssün -1 olarak verilmesi 1/x, 1/x kare vs. anlamına gelir. Örneğin pow (x,-2) 1/x kare demektir.

Java

Math.Log10
Örnekte bir sayısını küsürattan önce kaç basamağa sahip olduğu bulunmuş.

static int GetNumberOfDigits(decimal d)
{
    decimal abs = Math.Abs(d);

    return abs < 10 ? 1 : (int)Math.Floor(Math.Log10(decimal.ToDouble(abs)) + 1);
}

Yuvarlama İşlemleri
Yuvarlama (rounding) küsuratlı sayının tam sayıya çevrilmesi demek. Burada yuvarlama formüllerini gösteren güzel bir şekil var.

Round (Away FromZero) aşağıdaki gibi gerçekleştirilebilir
 
C
ceil
ceil() ve ceilf aynı işlevi görürler. Kabaca şöyledirler:
return ( (v < 0.0 ) ? v : (v +1) );

floor
floor() kabaca şöyledir.
return ( (v < 0.0) ? ( v - 1.0) : v ); 

round ve roundf
Bu metodlar verilen kayan sayıyı en yakın integer'a yuvarlar. 

Yuvarlama yönünü fegetround() ve fesetround() metodları ile değiştirebiliriz.

Sayıyı belli bir küsürata yuvarlamak için round() metodu dışında, buradaki  gösterildiği gibi floorf veya ceilf metodları kullanılabilir.

C++
Yuvarlama işlemi fesetround() ile yapılır. Örnek:
SQL
round metodu ile haç haneye kadar yuvarlama istendiği belirtilebilir.

Java
Math.Floor ve Math.Ceil metodları var.

Ceil benzeri bir işlem için buradaki örneğe bakabilirsiniz. Ceil'in tersine eksi sayılar daha da küçülüyor.

Yuvarlama yaparken kaç hane istediğimizi belirtmek istersek aşağıdaki kodu kullanabiliriz.
static Double round(Double d, int precise) {
BigDecimal bigDecimal = new BigDecimal.valueOf (d);
bigDecimal
= bigDecimal.setScale(precise, RoundingMode.HALF_UP);
return bigDecimal.doubleValue();
}

C#
Math.Round ile yuvarlamak yöntemi ve kaç hane istendiği belirtilebilir. Örnek:
Math.Round(value, 2, MidpointRounding.AwayFromZero);
Sayıyı Başka Bir Sayının Katı Olacak Şekilde Yuvarlama
Örneğin 201 gibi bir sayı 300'e yuvarlanır. 14 sayısı 100' yuvarlanır.
Algoritma aşağıdakine benzer
int round (int number,int multiple){
    int result = multiple;

    //If not already multiple of given number
    if (number % multiple != 0){
        int division = (number / multiple)+1;
        result = division * multiple;
    }
    return result;
}
Burada da benzer bir cevap var ancak genel bir algoritma sunmamış.

Bir diğer yöntem ise aşağıda. Kolay okunsun diye parantezleri renklendirdim.
#define ROUND_UP (x,multiple) ( ( (int) (x)+ (multiple -1) ) ) & ~ (multiple -1)

#define ROUNDA_DOWN (x,multiple) ( (int) (x) & ~ (multiple -1) )

Integer'a Cast Etme İşlemleri
Integer'a cast ederken dikkat edilmesi gereken bir nokta int sayılar daha küçük bit alanları ile temsil edildiklerinden dolayı her double sayı int'e cast edilemeyebilir. Edilirse aritmetik taşma (overflow) riski bulunur. Örneğin The Explosion of the Ariane 5 yazısında 64 bitlik bir double sayının 16 bitlik int'e cast edilmesi sonucu overflow olduğu ve yazılımdaki bu hatanın kazaya sebep olduğu anlatılıyor.
Java
How can I accurately determine if a double is an integer? sorusunda açıklandığı gibi Java dilinde bir double tüm integer değerleri temsil edebilir. Dolayısıyla aşağıdaki örnek işe yarar
if((int)d == d)
Ancak long değerleri sadece 2^52'ye kadar temsil edebilir. Long veri tipi 64 bit olduğu için 2^52^den büyük değeleri temsil edemez.
C++
Is int(doubleValue) guaranteed to be smaller or equal to doubleValue for positive values sorusunda açıklandığı gibi >=0 pozitif veya değeri 0 floating point sayıları int'e cast etme işlemi problemsiz çalışıyor.
Örnek : 

int i = int (doubleNumber) gibi.

Hash İşlemleri
Hash function for floats sorusunda verilen örnek ilginç.

String'e Çevirirken Gösterimsel Yuvarlama İşlemleri
Bir kayan nokta stringe çevirilirken yuvarlamaya maruz kalabilir. Yuvarlama yöntemini seçebilme yeteneği bazı dillerde mevcut.

Java 
Truncate a float and a double in java sorusunda anlatıldığı gibi DecimalFormat sınıfı kullanılabilir. Ayrıca DecimalFormat başlıklı yazıya göz atabilirsiniz.

Burada dikkat edilmesi gereken nokta :
DecimalFormat provides rounding modes defined in RoundingMode for formatting. By default, it uses RoundingMode.HALF_EVEN. 

bash
Floating point rounding in shell sorusuna verilen cevapta da aynı Java'daki gibi Half_Even yuvarlama yönteminin kullanıldığı görülebilir.
 
C
printf anladığım kadarıyla sadece yukarıya yuvarlama yapıyor.

String'e Çevirme İşlemleri
C
 
%e ile Üssü Şeklinde Gösterim
Bu gösterim şeklinde "e" veya "E" sembolü kullanılır ve üssü anlamına gelir. Örneğin 1e-9'un anlamı  0.000000001 iken -1e9 'un anlamı -1000000000.0'dur.

%f ile 6 küsurat hanesinin gösterimi
printf bir variadic metoddur (float için dikkat)
printf bir variadic metod olduğu için float veri tipi her zaman double'a çevirilip metoda parametre olarak geçilir.

double veya float göstermek için %f kullanılırsa bu seçenek ile aşağıda da görülebileceği gibi default olarak 6 küsurat hanesi gösterilir.

Eğer printf ("%.50f") kullanılırsa 50 küsurat hanesi gösterilir.

%4.2f ile toplam genişlik ve küsurat hanesinin gösterimi
Ayraçlar dahil olmak üzere toplam genişlik ve bu genişliğin kaç tanesinin küsurat olarak kullanılacağı belirtilir.
Eğer sayı formatlandıktan sonra toplam genişlik tutturulamadıysa aşağıdaki örneklerde görüldüğü gibi sola 0 eklenebilir.

printf ile sol tarafa sıfır ekleme
%04.1f ile ayraçlar dahil olmak üzere 4 karakter yazılır. Bunun 1 tanesi küsurat için kullanılır. Eğer toplam uzunluk 4'ten küçükse - örneğin 2.1 - gibi ise sol tarafa 0 eklenerek uzunluk tamamlanır.

toplam genişlik ve küsürat hanesinin dinamik olarak alma
Buradaki örnekte toplam genişlik ve küsürat genişliği dinamil olarak alınıyor. Toplam genişlik 6 ve küsürat genişliği 2 olarak veriliyor.
printf("%*.*f\n", 6 , 2,2345678.9);

printf için yanlış seçenek kullanılırsa gösterilen değer tanımsızdır
Eğer printf metodua yanlış seçenek kullanılırsa yazılan değerin ne olacağı tanımsızdır. C++ union to represent data memory vs C scalar variable type sorusuna verilen cevapta da okunabileceği gibi double yazdırmak için %d seçeneği kullanılırsa 8 byte'ın sadece ilk 4 byte'ı okunur ve beklenenden farklı bir değer gösterilir.


%g ile en kısa gösterimin seçilmesi


double değişkenleri yazdırmak için %e ,%E, %f, %F , %g veya %G seçeneklerinden herhangi birisi kullanılabilir.

%g ile en kısa gösterim şekli seçilir. Dolayısıyla bazen küsürat hanesinde sadece 0 varsa yazılmaz. Örneğin

printf("%g\n",5.0); //Çıktı olarak 5 yazar
printf("%g\n",5.1);//Çıktı olarak 5.1 yazar
%lf ile 80 bitlik long double değişkeninin yazılması

long double değişkenleri yazdırmak için %lf seçeneği kullanılabilir.

C++
C++ streamleri ile küsurat atama

std::cout << std::setprecision (6) << std::fixed << 3.14159265358979 << std::endl;
şeklinde kullanılabilir. Eğer azami küsurat isteniyorsa 6 yerine std::numeric_limits<double>::digits10 de kullanılabilir.

std::fixed gösterim ile scientific veya default gösterim yerine küsuratlı gösterimin istendiği belirtilir.

C++ streamleri ile genişlik atama
Floating point format for std::ostream sorusunda cevaplandığı gibi
std::cout << std::fixed << std::setw( 11 ) << std::setprecision( 6 ) << my_double;
yapılabilir. std::setw manipülatorü kalıcı değildir. Bir sonraki << işlemi ile temizlenir.
Eğer genişliği doldurmak için bir başka karakter kullanılmak isteniyorsa std::setfill( '0' ) da kullanılabilir.

boost ile formatlama
boost kütüphanesi de printf'e benzer bazı metodlar sunmaktadır. Örnek
#include <boost\format.hpp>
std::cout << boost::format("%11.6f") % my_double;
Java
Örneği buradan aldım.Aynı C'deki gibi "%8.2f" ile ayraç dahil olmak üzere en az 8 karakterlik minimum uzunluk verilebiliyor. Eğer minimum uzunluk yanlış verilmişse MissingFormatWidthException alınır.

İkilik String'e Çevirme İşlemleri
Java
How do I display the binary representation of a float or double? sorusuna verilen cevap
long binary = Double.doubleToLongBits(3.14159);
String strBinary = Long.toBinaryString(binary);
String'den Okuma İşlemleri
C++
Buradaki örnekten aldığım gibi stream'den okunabilir.

C
double atof (const char* str) kullanılarak yapılabilir.

atof
%lf ile double'ın okunması
printf ile double yazdırırken sadece %f kullanmak yeterliydi. Çünkü printf variadic olduğundan herşeyi double'a cast ediyordu. Ancak okurken scanf türü metodlara sadece pointer geçiyoruz. Dolayısıyla kaç byte okumak istediğimizi belirtmemiz gerekiyor. %lf ile double okumak istediğimizi belirtiyoruz.

strod
strod atof'tan daha gelişmiş. Gereksiz boşlukları atlıyor ve alabildiği kadar çok karakteri çevirmeye çalışıyor.

Byte'lardan Okuma İşlemleri
Java
How does Float.intBitsToFloat work? sorusunda Float.intBitsToFloat(int) metodunun nasıl çalıştığı gösterilmiş.
Can every float be expressed exactly as a double? sorusuna verilen cevapta ise intBitsToFloat metodu kullanılarak 32 bit ile temsil edilen tüm float'lat yaratılmış ve hepsinin double ile temsil edilebileceği ispatlanmış.

Burada dikkat edilmesi gereken nokta union tanımında bazı yerlerde long , int gibi veri tipler kullanılıyor.C/C++ ile bazı veri tiplerinin büyüklüğü kullanılan platforma göre değişebilir. Data Type Ranges(C++) sayfasında bazı örnekler var. Aşağıdaki tabloda çok kullanılan büyüklükleri görmek mümkün.

long (long int, unsigned long vs.) : genellikle 4 byte
long long (unsigned long long ) : genellikle 8 byte

Ancak double için aşağıdaki gibi bir union tanımlamak her zaman doğru olmayabilir.

union udouble {
  double d;
  unsigned long u; // her zaman doğru olmayabilir.
}
C++
C++ ile elimizde byte[] varsa How can I convert 4 bytes storing an IEEE 754 floating point number to a float value in C? sorusunda açıklandığı gibi 
float f;
char * p = (char *)&f;
p[0] = byte1; 

p[1] = byte2;
şeklinde çevirilebilir.
Özel Sabitler
Epsilon
Epsilon kelime anlamı olarak sıfıra en yakın rakam demek.
C
<cfloat> içinde tanımlı olan FLT_EPSILON 1'den büyük olan en küçük pozitif sayıyı elde etmek için kullanılabilecek değeri gösterirYani tanımın tersine  1 + FLT_EPSILON = 1'den büyük en küçük pozitif sayı anlamına geliyor.
C++
std::numeric_limits<float>::epsilon() 'da yukarıdaki işlemi C++ için yapar. Bu sabit genellikle iki kayan noktayı karşılaştırmak için kullanılır.

numeric_limits<float>::min() : en küçük pozitif normalized değerleri döner. Bu sayı FLT_MIN'dir. Örnek:

C#
Single.Epsilon C'nin aksine tanıma uygun olarak - en küçük pozitif sayının kendisini verir. 

Özel Sayılar
Infinity
C++
Sonsuzu temsil etmek için.

Eksi sonsuzdan bir önceki en küçük negatif değeri almak için numeric_limits<float>::lowest() kullanılır.  Örnek:
 
Java
Geçersiz bir sayıyı temsil etmek için Float.NaN 

Sıfır
Sayının eksi sıfır veya artı sıfır olduğunu anlamak için genel bir çözüm olarak aşağıdaki gibi yapılabilir.
if (1 / x > 0)
// +0 here
else 
// -0 here 
Java
Sayının başındaki bite bakılabilir. Örnek:
float f = -0.0f;

if (Float.floatToIntBits(f) == 0x80000000) {
System.out.println("Negative zero");
}
Sıfıra Çok Yakın Bir Sayı İle Bölme İşlemleri
C++
Bölen sıfır değil ancak sıfıra çok yakın bir sayı olursa veya bölme işleminin sonucu çok büyük olursa Infinity elde edilebilir.

Java
Burada açıklandığı gibi System.out.println(0/0); java.lang.ArithmeticException: / by zero exception'a sebep olurken System.out.println(6.199/0); olmuyor ve infinity dönüyor. Sebebi ise infinity integer olarak temsil edilemiyor ve integer bölme işlemi exception atmak zorunda, ancak infinity kayan nokta olarak temsil edilebiliyor ve IEEE 754 standardı bu durumda exception atmamayı tercih etmiş.

Exception Yakalama
Bir sayının sıfıra bölünmesi hardware trap'e sebep olur. Bu trap ise işletim sistemi tarafından yakalanır.

Exceptionlarla ilgili metodlar fenv.h içinde tanımlı.
C ile fetestexcept()  C++ ile ise std::fetestexcept() metodlarını kullanarak bazı kayan nokta işlemlerindeki hataları yakalamak mümkün.fetestexcept() ile hata bitlerini almadan önce feclearexcept() ile bitleri temizlemek gerekiyor. Örnek programı buradaki soruyu okurken gördüm. Çıktı olarak aşağıdaki satırları veriyor.

Attempted to divide by zero:
Detected FE_DIVBYZERO
Attempted sqrt(-1):
Detected FE_INVALID

İki Sayıyı Karşılaştırma
C++
Bir çok örnek var. Gördüğüm en kolay anlaşılır olanını Most effective way for float and double comparison sorusundan aldım.
Bir başka örnekte ise benzer bir şey yapılmış.

Fixed Point
Floating point'e baktıktan sonra Fixed Point ile ilgili de biraz yazmak faydalı olabilir. Fixed point artık pek kullanılmıyor. Amacı, örneğin 32 bitlik bir integer'ın 24 bitini ana sayı 8 bitini ise ondalık alan olarak kullanmak.

Arbitrary Precision Arithmetic
Konuyu Arbitrary Precision Arithmetic başlıklı yazıya taşıdım.

Unix Shell Script
Unix kabuğunda aritmetik hesaplama yapmak için örnek: bc komutu "basic calculator" anlamına geliyor.

Hiç yorum yok:

Yorum Gönder