15 Ekim 2018 Pazartesi

IEEE 754

Not :
İngilizcede precision ve accuracy diye iki kelime var. Türkçeye tam nasıl çevrilir bilemedim.
Kelime anlamı olarak precision ile hassasiyet yani ölçülen değerlerin birbirine yakınlığı, Accuracy  ile de doğruluk yani istenilen değere yakınlık, isabet kast ediliyor.

Precision : Bir sayının içerdiği tüm önemli rakamlara (significant digits/figures) denilir. Önemli rakamlarım sayarken mesela en soldaki sıfırlar sayılmaz. 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. Örnek:  
Velocity 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 
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".
IEEE 754 aynı zamanda IEC 559 olarak ta bilinir

IEEE 754'ten Önce
IEEE 754'ten önce de küsuratlı işlemler yapılabiliyordu. Açıklaması şöyle
The ENIAC did not have an FPU; it used a ten-digit decimal notation, with no floating-point features.

The IBM 704 had one as standard equipment in 1954. It had various floating-point add, subtract, multiply and divide instructions, but no square-root function.

The IBM 650, around the same time, had an optional floating-point module. As best I can tell, it too supported only addition, subtraction, multiplication, and division.
Bir başka açıklama şöyle. Yani 16 bit, 32 bit için daha önceden de farklı şeyler vardı
Q : Did any PC software floating point use non-IEEE format?
A : Turbo Pascal 2.x and the "ordinary" version of Turbo Pascal 3.x used a six-byte floating-point format which offered higher precision than an IEEE single-precision float, 
...
Several earlier 16-bit floating point formats have existed including that of Hitachi's HD61810 DSP 2 of 1982, Scott's WIF [3] and the 3dfx Voodoo Graphics processor. [4]

Tüm Sayılar Temsil Edilemez
Kayan noktalarda unutulmaması gereken bir nokta tüm sayıları temsil etmenin imkanı yoktur!
Örneğin C#'ta birbirine çok yakın ama farklı iki değişken tanımlayalım
var x = -6.2845230102539063;
var y = -6.2845230102539098;
Bu iki değişkeni tam olarak yazdırmaya kalkarsak çok farklı şekillerde temsik edildiklerini görebiliriz.
Console.WriteLine(DoubleConverter.ToExactString(x));
Console.WriteLine(DoubleConverter.ToExactString(y));
Çıktı alarak şunu alırız.
-6.28452301025390625
-6.284523010253909802713678800500929355621337890625

IEEE754 Desteği
C veya C++ IEEE 754 desteğini şart koşmuyor.
The value representation of floating-point types is implementation-defined.
Eğer IEEE754 standardının kullanılıp kullanılmadığını bulmak istiyorsak std::numeric_limits::is_iec559() metodunu kullanırız.

Java ve C# IEEE754 desteği ile geliyor. Ancak Java IEEE754 desteğini Java 1.2'den sonra gevşetmiş durumda. Ara sonuçların (intermediate results) daha fazla bit kullanmasına izin veriyor. strictfp Anahtar Kelimesi yazısına bakabilirsiniz.

Fraction
IEEE754 yerine neden fraction kullanılmadığının açıklaması şöyle
When adding or subtracting fractions, you need to find the least common multiple of the two denominators. That's an expensive operation, much more expensive than adding or subtracting floating points, which just requires shifts.

Multiplication is also more expensive, because now you need to multiply two numbers instead of just one. Similarly for division.

Also, the numerator and denominator of a fraction will eventually grow large, which means you won't be able to store them in the limited memory of an 8-bit system. Floating point deals with this by rounding.

So: It's more expensive, there's no way to limit the memory used for truly general applications, and scientific applications are geared towards using floating point, anyway.

Double
IEEE754- Double yazısına taşıdım.

Float
IEEE 754 - Float yazısına taşıdım.

Half Precison
IEEE 754 Half isimli 16 bit için bir başka standart var. Half Precision Arithmetic yazısına taşıdım.

Denormal Number
IEEE 754 - Denormal Number yazısına taşıdım.

Sayılarda Seyrelme - Sparse Hale Gelme
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). Burada 0'dan uzaklaştıkça 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. Eğer belli bir aralığı dolaşmak istersek aşağıdaki gibi int kullanmak daha makul olabilir.

for (int i=100; i>=0; --i)
{    
    float f = i/100.0f;
    ...
}

Intel 80 bit floating point
IEEE 754 Extended Precision yazısına taşıdım.


Yuvarlama İşlemleri
Konuyu IEEE 754 ve Yuvarlama başlıklı yazıya taşıdım.

Integer'dan Cast Etme İşlemleri
C++
Özellikle bazen problem olabiliyormuş. Örnekte
#include <stdio.h>

int main() {
    int x = 2147483647;
    printf("%f\n", (float)2147483647);
    printf("%f\n", (float)x);
    return 0;
}
çıktı olarak şu değerleri alırız.
2147483648.000000
2147483647.000000
Sebep ise şöyle gösterilmiş. 
  • Casting int to float, when the magnitude of the int value is less than FLT_MAX and the int is not a representable value for float, causes either the next-highest or next-lowest float value to be selected, and which one is selected is implementation-defined. (6.3.1.4/2)

Anladığıma göre float tam sayı için daha az bit kullanıyor. Range olarak int değeri kapsasa bile bit olarak sığdıramıyor ve yuvarlama ihtiyacı beliriyor.

Integer'a Cast Etme İşlemleri
Integer'a cast ederken dikkat edilmesi gereken bir nokta, int sayılar daha küçük bit uzunlukları 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
Konuyu IEEE 754 ve String'e Çevirme başlıklı yazıya taşıdım.

String'den Okuma İşlemleri
Konuyu IEEE 754 ve String'den Okuma başlıklı yazıya taşıdım.

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
Konuyu IEEE 754 Özel Sabitler başlıklı yazıya taşıdım.

Özel Sayılar
Konuyu IEEE 754 Özel Sayılar başlıklı yazıya taşıdım.

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
Konuyu IEEE754 ve Exception başlıklı yazıya taşıdım.

İki Sayıyı Karşılaştırma
C++
IEEE 754 ve İki Sayıyı Karşılaştırma yazısına taşıdım.

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.


Hiç yorum yok:

Yorum Gönder