Jump to page: 1 2
Thread overview
Kesirli sayı işlem sıralarının etkileri
Feb 09, 2012
Salih Dinçer
Feb 09, 2012
Salih Dinçer
Feb 10, 2012
Salih Dinçer
Feb 10, 2012
Salih Dinçer
Feb 10, 2012
Salih Dinçer
Feb 11, 2012
Salih Dinçer
Feb 14, 2012
Salih Dinçer
Feb 16, 2012
Salih Dinçer
February 09, 2012

Çok ilginç bir hata!

Ben de hemen şimdi şu şekilde denedim:

foreach (i; 0 .. 1_000_000) toplam+=1.000_001;

ve sonuç 22 milyon çıktı...:)

İşin ilginci +=3 yaptığımızda da 24 milyon, +=6 yaptığımızda ise 26 milyon çıkıyor. Bana ortak nokta 3 ve üçün katları gibi geldi.

Başarılar...

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 09, 2012

Şimdi ise şöyle denedim:

'toplam = 0 ise;
foreach (i; 0 .. 1_000_000) toplam+=1.000_000_1; sonucunda:
1000000.062500 çıkıyor...'

Bu belki normal çünkü virgülden sonra 7 hane. Ama 6 hande de aynı sonucu alıyoruz. Şu durumda ise:

'toplam = 0 ise;
foreach (i; 0 .. 1_000_000) toplam+=1.000_1; sonucunda:
1000000.250000 çıkıyor...'

Son olarak ise şöyle denedim ve matematiğim çok kuvvetli değil ama herhalde yanlış öyle değil mi?

'toplam = 0 ise;
foreach (i; 0 .. 1_000_000) toplam+=1.1; sonucunda:
1110920.500000'

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 09, 2012

Koşut İşlemler bölümüne koşun reduce ve koşut map kullanım örnekleri de eklemek üzereyim. std.parallelism'in belgesinde koşut olarak işletilen işlemlerin sıralarının önceden kestirilememesi ve bu yüzden sonuçların farklı çıkabileceği ile ilgili bir uyarı var.

O durumun bir örneği:

import std.stdio;

void main()
{
   writefln("float'un duyarlığı %s hanedir.", float.dig);

   float toplam = 20_000_000;
   foreach (i; 0 .. 1_000_000) {
       toplam += 1;
   }

   writefln("%f", toplam);
}

20 milyon değeri ile başlıyoruz ve bir milyon kere 1 değerini ekliyoruz. Buna rağmen sonuç 21 milyon olmuyor:

'float'un duyarlığı 6 hanedir.
20000000.000000
'

Eğer toplam'ı 0 değeri ile başlatır ve 20 milyonu sonda eklersek bu sefer 21 milyon oluyor:

   float toplam = 0;
   foreach (i; 0 .. 1_000_000) {
       toplam += 1;
   }
   toplam += 20_000_000;

Çıktısı:

'float'un duyarlığı 6 hanedir.
21000000.000000
'

Tabii bu D ile ilgili bir sorun değil, sayıların bilgisayarlarda gösterimiyle ilgili. Kesirli sayı türünün duyarlığının 6 olduğu her ortamda vardır. Aynı durumla double ve real türlerinde de karşılaşılır ama onların duyarlığı daha fazla olduğu için 20 milyondan daha büyük değerlerle denemek gerekir.

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 10, 2012

Tam anlayamıyorum ve hiç de bulaşmak istemiyorum...:)

Benim için tam sayılar (integer), kalansız bölme (mod) ve mantıksal işleçler (bitwise operators) yeter de artıyor bile. 3B olaylarına da girmediğim için aklım rahat. Ama sanırım buradaki olay yazılımsal. Yoksa şu sevdiğim makaledeki gibi donanımsal olmasa gerek: http://www.ktemo.org/index.php?option=com_docman&task=doc_view&gid=75&Itemid=137

Sevgiler, saygılar...

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 10, 2012

Teşekkürler Hocam,

Sayende çok şey öğreniyoruz. Hatta bu kayan nokta (http://tr.wikipedia.org/wiki/IEEE_754-2008) denilen şeyi bugüne kadar okurdum da yukarı/aşağı yuvarlamalar dışındaki duyarlılığı beni çok ilgilendirmezdi. Meğer neler varmış neler. Peki biz yuvarlama şeklini değiştirebiliyor muyuz? Anladığım kadarıyla ne sıfıra ne de aşağıya yuvarlıyor, değerin fazla çıkması bize hep yukarı yuvarladığını gösteriyor!

Tabi ben, doğal olarak virgüllü sayılar taktım! Madem 'float' ve 'double' gibi değişkenler kullanıyoruz. O zaman doğru hesaplasın canım, hangi devirde yaşıyoruz...:)

Ben de çözüm olarak döngüde toplananın virgülünü kaldırım (10 ile çarpıp) sonra tekrar eski haline getirip (10'a bölüp) durumu kurtardım; sanırım!

Codehttp://ddili.org/forum/unb_lib/designs/modern/img/arrow_right.png

   import std.stdio;
   float çarpan, toplam = 20_000_000;

void main() {
   writeln ("Toplam duyarlılık: ", toplam.dig);
   writefln ("Olması gereken: %f", (1_000_000 * 1.1) + toplam);

   çarpan = 0;
   foreach (i; 0 .. 1_000_000) çarpan += 1.1;  // Virgüller hep hatalı!
   writefln("\n%f", çarpan + toplam);

   çarpan = 0;
   foreach (i; 0 .. 1_000_000) çarpan += 11;
   writefln("\n%f", (çarpan / 10) + toplam);
}

Çıktıhttp://ddili.org/forum/unb_lib/designs/modern/img/arrow_right.png
'Toplam duyarlılık: 6
Olması gereken: 21100000.000000

21110920.000000

21100000.000000'

Başarılar...

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 09, 2012

Yirmi milyon ile birin eklendiği durumdaki hatanın açıklaması daha basit: O durumda yalnızca 1 duyarlığın altında kaldığı için onun eklenmesi etkisiz oluyor. İşin ilginci, hem 20_000_000 hem de 1 float türünde tam olarak ifade edilebiliyorlar.

Alıntı (Salih Dinçer):

>
>  foreach (i; 0 .. 1_000_000) toplam+=1.000_001;
> ```


Yukarıdaki durum ise daha karışık çünkü işin içine virgülden sonraki değerler de giriyor. Virgülden sonraki bazı değerler tam olarak ifade edilemezler (örneğin 0.000_001). O yüzden o örnekte işin içine başka etkiler de giriyor.

Ali

-- 
[ Bu gönderi, <http://ddili.org/forum>'dan dönüştürülmüştür. ]
February 10, 2012

Bölüm sonucunun ifade edilmesindeki zorluğa katılıyorum. İşte analog ve digital ayrımındaki keskin çizgi...

Ancak biz burada üst üste toplama yapmaktayız. Hata almamıza anlam veremiyorum. Emin olmak için değerleri biraz arttırdım (double üzerinden), yine saçma sapan sonuçlar aldım. Üstelik işlem yapılan bit oranı arttıkça sonucu almak dramatik bir şekilde gecikiyor: (biliyorum, bu bir deneme yoksa tek satırlık çarpma yeterli)

'salih@DB-N150-N210-N220:~/d.ders$ time ./float
Toplam duyarlılık: 15
Olması gereken: 20.110.000.000,000000

20.109.999.999,831760

20.110.000.000,000000

real 0m3.605s
user 0m3.588s
sys 0m0.004s'

Neyse, çok güzel bir ders konusu oldu.

Sevgiler, saygılar...

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 10, 2012

Alıntı (Salih Dinçer):

>

Ama sanırım buradaki olay yazılımsal. Yoksa şu sevdiğim makaledeki gibi donanımsal olmasa gerek

Sanırım hem donanım hem de yazılım IEEE floating point standardını uyguluyor. 32-bitlik kesirli sayı gösteriminde fraction içinde 20 milyon gibi büyük bir sayı otururken 1'e yer yok:

http://en.wikipedia.org/wiki/IEEE_754-1985

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 10, 2012

(Not: Bunları yazarken üstten taşma kavramını gözardı ediyorum.)

(Ek: "binde 4"ü "250'de bir", "binde 3"ü de "300'de bir" olarak değiştirdim.)

Evet, virgüllere geçince olayın içine ifade edilememe kavramı da giriyor. Bunu onlu sistemde de görebiliriz. Bir değere "250'de bir" eklediğimizde tek sorun yaşama olasılığımız vardır: duyarlık. Eğer sonuç virgülden sonra 2 hane ile kısıtlıysa, örneğin 12345 gibi bir değere "250'de bir" eklediğimizde 12345.00 elde ederiz. Ama bu, yalnızca duyarlık nedeniyledir, çünkü 0.004 değeri onlu sistemde tam olarak ifade edilebilir. Duyarlık arttırılıp virgülden sonra 3 haneye çıktığında sonuç 12345.004 olur ve yine tam doğrudur.

Şimdi tam olarak ifade edilememe kavramına geçiyorum. Yine onlu sistemdeyiz. Bir değere "300'de bir" eklemek istediğimizde artık iki tür sorun var: duyarlık ve tam olarak ifade edilememe. 12345 sayısına "300'de bir" eklendiğinde ve duyarlık virgülden sonra 2 hane olduğunda yine 12345.00 elde ederiz. Ama duyarlığı ne kadar arttırırsak arttıralım, tam olarak ifade edilememe sorununu aşamayız. 12345.00333333333 bile yazsak bir yerden sonra hatalıdır.

İşte aynı sorun ikili sistemde de var. Bilgisayarlarda 0.1 gibi bir değer tam olarak ifade edilemez. Virgülden sonra tam olarak ifade edilebilen değerler, şu değerlerin karışımı olan değerlerdir:

'1/2 = 0.5
1/4 = 0.25
1/8 = 0.125
vs.'

Salih, eğer virgülden sonraki değerlerle denemek istiyorsan ve yalnızca duyarlıkla ilgileniyorsan virgülden sonrası için 0.625 gibi tam olarak ifade edilebilen kesirli değerler kullanmanı öneririm.

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 10, 2012

Daha ayrıntısını bilmiyorum ama değerlerdeki hataları görmek için şöyle bir programla oynanabilir:

import std.stdio;

void dene(T)(size_t adet, T artış)
{
   writefln("--- %s denemesi ----", T.stringof);

   T toplam = 0;
   foreach (i; 0 .. adet) {
       toplam += artış;
       writefln("%s: %.70f", i, toplam);
   }
}

void main()
{
   immutable adet = 20;
   dene(adet, 0.1F);
   dene(adet, 0.1 );
   dene(adet, 0.1L);
}

Çıktısı:

'--- float denemesi ----
0: 0.1000000014901161193847656250000000000000000000000000000000000000000000
1: 0.2000000029802322387695312500000000000000000000000000000000000000000000
2: 0.3000000119209289550781250000000000000000000000000000000000000000000000
3: 0.4000000059604644775390625000000000000000000000000000000000000000000000
4: 0.5000000000000000000000000000000000000000000000000000000000000000000000
5: 0.6000000238418579101562500000000000000000000000000000000000000000000000
..
'

Öncesinde o kadar az sayı eklendiği için 0.5 değerinde durumu kurtarıyor. (Daha önce de söylediğim gibi, 0.5 ikili düzende tam olarak gösterilebilir.)

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

« First   ‹ Prev
1 2