24 Şubat 2020 Pazartesi

IEEEE 754 - Double

Double
Double'ın bir diğer ismi ise Double-precision floating-point format.

Double için kullanılan 64 bit şöyle bölümleniyor.
   1 bit|11bit    |52 bit
    sign|exponent |mantissa veya significant
Türkçeleri şöyle:
Sign = Yön, Exponent = Üst, Mantissa = Kök

32 bit tüm tamsayılar, double ile temsil edilebilir.

Exponent
Açıklaması şöyle. Yani bu alanın 11 bit olması tesedüf değil.
For the 64-bit format, the main consideration was range; as a minimum, the desire was that the product of any two 32-bit numbers should not overflow the 64-bit format. The final choice of exponent range provides that a product of eight 32-bit terms cannot overflow the 64-bit format — a possible boon to users of optimizing compilers which reorder the sequence of arithmetic operations from that specified by the careful programmer.
Significant Figure
Not : Bir çok yazıda precision yerine "significant figure" kelimesi de kullanılıyor. Ben bu yazıda precision kelimesini kullandım, çünkü Fizik Bilimindeki significant kavramı ile karışmasın istedim.

Fizik Biliminde significant kelimesinin tanımı şöyle. Yapılan ölçümde kaç haneye kadar emin olduğumuzu gösterir.
  1. Significant figures by definition are the reliable digits in a number that are known with certainty.”
  2. “A significant figure is the one which is known to be reasonably reliable.”
Precision
Tablo olarak şöyledir.
             bits        range                       precision
  float        32        1.5E-45   .. 3.4E38          7- 8 digits
  double       64        5.0E-324  .. 1.7E308        15-16 digits
  long double  80        1.9E-4951 .. 1.1E4932       19-20 digits
Precision toplam hane sayısı anlamına gelir. Bu veri tipi ile 16-17 precision 'a kadar sayıları saklamak mümkün.  Örnek'te 17 hane var.
569.23230682688904 - Toplam 17 hane
Ya da şöyle
double: 0.333333333333333 - Toplam 17 hane , sıfır hariç
C dili precision değerine erişmek için bazı macrolar tanımlamış. Bunlar şöyle. İlk sütun C'deki tanımlı minimum değer, ikincisi ise IEEE 754 standardındaki tanıımlı değer. double için C 10 hane, IEEE 754 ise 17 hane belirtir. Bir çok derleyici IEEE 754 uyumlu olduğu için ikinci değeri kullanır.
FLT_DECIMAL_DIG   6,  9 (float)                           (C11)
DBL_DECIMAL_DIG  10, 17 (double)                          (C11)
LDBL_DECIMAL_DIG 10, 21 (long double)                     (C11)
DECIMAL_DIG      10, 21 (widest supported floating type)  (C99)
Tam Sayı
"Integer digits",  "Integral digits" olarak adlandırılır.

Küsurat
"Fraction Digits", "Fractional Digits", "Fractional Part" veya çok nadiren de olsa "Decimal" olarak adlandırılır. Cümle içindeki örnek şöyle
For instance, in the number 31.415, there are two integer digits and three fraction digits.
Round Trip
Şöyle yaparız.
printf("%.17g", number)
Örnek
Elimizde şöyle kodlar olsun
C++ kodu
cout.precision(17);
for (double i = 0.0; i < 3; i = i + 0.1) {
  cout << fixed << i << endl;
}
Java kodu
for (double i = 0.0; i < 3; i = i + 0.1) {
  System.out.println(i);
}
Bu 3 kod sanki farklı sonuçlar veriyor gibi görünebilir. Ancak esas fark double değerin string'e çevrilirken geçtiği işlemden kaynaklanıyor. Örneğin C ve Java double'ın sadece bir komşu değerden farklı olduğunu gösterecek kadar basamak kullanmasını gerektiriyor.

Significant Precision
Significant sağdaki sıfırların üst şeklinde yazıldıktan sonra kalan rakamlardır. Örneğin 1500 sayısı 1.5 x 10 ^ 3 şeklinde yazılabilir. Bu durumda Significant 1.5 olur. Bundan sonra tam hane sayısı olarak yazacağım.

1. C Dili
C dili significant precision değerine erişmek için bazı macrolar tanımlamış. Bunlar şöyle. İlk sütun C'deki tanımlı minimum değer, ikincisi ise IEEE 754 standardındaki tanıımlı değer. double için C 10 hane, IEEE 754 ise 15 hane belirtir. Bir çok derleyici IEEE 754 uyumlu olduğu için ikinci değeri kullanır.
FLT_DIG   6, 6 (float)
DBL_DIG  10, 15 (double)
LDBL_DIG 10, 18 (long double)
 Açıklaması şöyle.
C provides DBL_DIG, DBL_DECIMAL_DIG, and their float and long double counterparts. DBL_DIG indicates the minimum relative decimal precision. DBL_DECIMAL_DIG can be thought of as the maximum relative decimal precision.
Bu macrolara bakınca değerleri şöyle
FLT_DIG 6 or greater
DBL_DIG 10 or greater
LDBL_DIG 10 or greater
2. C++
Eğer derleyici IEEE 754 uyumlu ise  tam sayı hanesi ise 15'tir. Yani noktanın sol tarafında en fazla 15 tane sayı olabilir. Yani tüm 15 haneli tam sayılar double'a çevrilip tekrar tam sayı haline gelebilir.
int digit10=std::numeric_limits<double>::digits10; // ==15
3. Kendimiz
Şöyle hesaplarız.
log10(2^53) = 15.95
Küsuratlı sayı olamayacağı için sayı aşağıya yuvarlanır ve 15 olur.

Exponent
Exponent alanı 1023'e göre referans alınır. Bunan exponent bias denir. Ö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

C++'ta numeric_limits içindeki radix, digits, min_exponent, max_exponent, min_exponent10, max_exponent10 metodları da kullanılabilir. Double için şu çıktıyı alırız.
radix : 2
digits : 53
min_exponent : -1021
max_exponent : 1024
min_exponent10 : 308
max_exponent10 : -307

Sonsuz Sayısı
Örnek - Java
Şöyle yaparız.
double i = Double.POSITIVE_INFINITY;
Sonsuz Sayısını Sayıyı Artırmak
Açıklaması şöyle.
...because adding 1 to a floating-point value that is sufficiently large will not change the value, because it doesn't "bridge the gap" to its successor.
Örnek
Şu kod sonsuz döngüye sebep olur.
double i = Double.POSITIVE_INFINITY;
while (i == i + 1) {}

En Büyük Sayı
Örnek - C Dili
Double ile temsil edilebilecek en büyük sayı DBL_MAX  sabiti ile bulunur. Yazdırırsak şu çıkar.
1.7976931348623157e+308
Örnek - Java
Şöyle yaparız
double i = Double.MAX_VALUE;
En Küçük Sayı
Double ile temsil edilebilecek en küçük sayı sabiti -DBL_MAX ile bulunur. C++ ile şöyle yaparız.
std::numeric_limits<double>::lowest()
Java'da şöyle yaparız
The smallest negative number for Double is -Double.MAX_VALUE
Sıfıra Yakın En Küçük Sayı
Double ile sıfıra en yakın sayı  DBL_MIN ile bulunur. C++ ile şöyle yaparız.
std::numeric_limits<double>::min()
NaN Sayısı
Örnek - Java
Şöyle yaparız.
double i = Double.NaN;
NaN Sayısını Artımak
Açıklaması şöyle.
...results in an infinite loop, because NaN is not equal to any floating-point value, including itself
Örnek
Şu kod sonsuz döngüye sebep olur.
double i = Double.NaN;
while (i != i) {}
Karşılaştırma
Yöntemler şöyle
1. Her sayı belli bir küsurat hanesine yuvarlanır ve >, < işlemine sokulur. 
org.apache.commons.math3.util.Precision sınıfı kullanılabilir

Örnek
Şöyle yaparız
double firstValue = 10.2f;
double secondValue = 10.3f;
       
if(firstValue > secondValue){
  System.out.print("First Value and second value are not equal");
}
2. Epsilon veya diğer ismiyle tolerans değeri kullanılarak karşılaştırma yapılır.
Bu yöntemi kullanıyorsak metod isimleri şöyle olabilir. Metod isimleri için Almost, Nearly,Approximately gibi isimler de kullanılabilir
NearlyEqual
NearlyNotEqual
DefinitelyGreater
NearlyGreaterOrEqual
DefinitelyLess
NearlyLessorEqual
Yine org.apache.commons.math3.util.Precision sınıfı kullanılabilir
Veya com.google.common.math.DoubleMath sınıfı kullanılabilir
3. Belirtilen ULP (x ve y değeri arassındaki kaç tane double olduğu) kadar yakın olup olmadığının karşılaştırması yapılır
Yine org.apache.commons.math3.util.Precision sınıfı kullanılabilir


Küsurat Hanesine Yuvarlama
Şöyle yaparız.

Sum İşlemi
Bir çok double sayısını ardarda toplayınca aslında hataları da eklemiş oluyoruz. Yani sapma daha çok oluyor. Bu gibi durumlar için Kahan summation kullanılabilir. Java'daki DoubleStream bu algoritmayı kullanıyor.

Hiç yorum yok:

Yorum Gönder