Forum: Ders Arası RSS
Dizi değerleri neden bozuk?
acehreli (Moderatör) #1
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: Dizi değerleri neden bozuk?
Bu sorun D.learn forumunda gösterildi. Aşağıdaki soruyu oradan uyarladım.

Aşağıdaki program dizinin 5 elemanına değerler atıyor. foreach'in sayaç olanağından yararlanıldığı için i 0'dan 4'e kadar değerler alır. Sonuçta elemanların şöyle olmaları bekleniyor:

[ -3, -2, -1, 0, 1 ]

import std.stdio;
 
void main()
{
    double[5] dizi;
 
    int ortaDeğer = 3;
 
    foreach (i, ref eleman; dizi) {
        eleman = i - ortaDeğer;
    }
 
    writeln(dizi);
}

Ama eleman değerleri öyle olmuyor. Neden?

Ali
erdem (Moderatör) #2
Üye Tem 2009 tarihinden beri · 978 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Hımm. Programı çalıştırmadan ve cevaba bakmadan rastgele fikirler..

import std.stdio, std.random;
 
void main () {
    double[5] dizi;
 
    foreach(ref eleman; dizi) {
        eleman = uniform(5, 15);
    }
 
    foreach(i; 0 .. dizi.length) {
        writeln(dizi[i]);
    }
}

Örneğin bu program çalışıyor. Burada foreach ile ilgili bir hata yok sanırım. Çünkü dershanede bu şekilde örnekler vardı.

    foreach (sayaç, eleman; dizi) {
        writeln(sayaç, ": ", eleman);
    }

Sabit genişlikli bir diziyi bir işleve parametre olarak geçtiğimizde ya da döndürdüğümüzde tüm elemanlar kopyalanıyordu. Ama böyle bir durum da gözükmüyor.

i'nin ilklendirme değeri ne olabilir. Eğer sayaç olarak kullanılacaksa arttırılması gerekmez mi. Ben i'yi hataya yakın buluyorum. Ve ilklendirme değeri geçersiz bir değerse hatalı sonuç elde edebiliriz diye düşünüyorum.

Birazdan programı çalıştırıp deneyeceğim merak etmeye başladım! :-D

Programı çalıştırdıktan sonra i ve eleman'ın türleri farklı. Birisi double birisi int.

Programı şu şekilde düzelttikten sonra çalışıyor:

import std.stdio;
 
void main()
{
    double[5] dizi;
    int ortaDeğer = 3;
    
    foreach (int i, ref eleman; dizi) {
        eleman = cast(double) (i - ortaDeğer);
        
    }
 
    writeln(dizi);
}
Bu mesaj erdem tarafından değiştirildi; zaman: 2011-04-28, 10:45.
canalpay (Moderatör) #3
Kullanıcı başlığı: Can Alpay Çiftçi
Üye Tem 2009 tarihinden beri · 1133 mesaj · Konum: İzmir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #1
yanıta bakmadan ve bilmeden serbest atış yapıyorum.
Tahmin:

eleman dizi[indis] demek. uint değerinde indis. indis - olunca eksi değer alamadığından en yüksek değerden başlıyor.
erdem (Moderatör) #4
Üye Tem 2009 tarihinden beri · 978 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
import std.stdio;
 
void main()
{
    double[5] dizi;
 
    int ortaDeğer = 3;
 
    foreach(int i; 0 .. dizi.length) {
        dizi[i] = i - ortaDeğer;
    }
     
    writeln(dizi);
}

Bu programda da belli oluyor. Orada i'nin başına int koymayınca hemen hemen aynı sonucu veriyor :)

Ama ben yazsam böyle yazardım herhalde.
acehreli (Moderatör) #5
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Evet, sorun foreach sayacının size_t türünde olması. Ortamına göre değişebiliyor: 32 bit ortamda uint, 64 bit ortamda ulong. Ama sorun bit sayısında değil, sayacın işaretsiz (unsigned) bir tür olmasında.

C'den gelen kurallar gereği, işaretsiz türlerin bulunduğu işlemlerin türü de işaretsizdir. Yani aslında 'i - ortaDeğer' işleminin sonucu işaretsizdir. Alttan taşarsa işaretsiz türün büyük bir değerini edinir.

Dizi elemanı double olduğu için de o büyük değer aynen korunuyor.

Bence foreach sayacının türünü Erdem'in gösterdiği gibi açıkça int yazmak iyi bir çözüm.

İşin başka kötü tarafı, dizinin türü baştan int olsa bu hatayı hiç farkedemiyoruz; çünkü işaretsiz sonucumuz o zaman int'e otomatik olarak dönüşüyor ve ikiye tümleyen aritmetiği nedeniyle doğru int değerini üretiyor. Sonuçta da yıllardır bu hatayla doğru olarak işlemekte olan program, dizi türü double yapılınca birden bire bozuluyor. :-/

Ali
erdem (Moderatör) #6
Üye Tem 2009 tarihinden beri · 978 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Bir de Can'ın da bahsettiği gibi eğer uint işaretsiz bir değer kullanırsak. Örneğin yukarıdaki ortadeğer ve i'yi işaretsiz olarak tanımlarsak bir taşma olduğu için cast(double) ile de kurtaramıyoruz :)
canalpay (Moderatör) #7
Kullanıcı başlığı: Can Alpay Çiftçi
Üye Tem 2009 tarihinden beri · 1133 mesaj · Konum: İzmir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Bir de Can'ın da bahsettiği gibi eğer uint işaretsiz bir değer kullanırsak. Örneğin yukarıdaki ortadeğer ve i'yi işaretsiz olarak tanımlarsak bir taşma olduğu için cast(double) ile de kurtaramıyoruz


Ben sadece nedeni açıkladım. Çözümü söylemedim. uint türünde olduğu için hata olduğuhnu söyledim.

söylediklerimi daha ayrıntılı yazarsam:

Ben dizinin aralık(daha doğrusu indis) olarak uint aldığını uint ile - gibi işaretin gerekli olduğu bir işlem yapıldığı için hata olduğunu söyledim. foreach doğal olarak indisin değerinde değişken tutuyor ve hata oluşuyor dedim. Aslında hata denmesininde doğru olmadığını çünkü işaretsiz türlerin doğal davranışının değişkene sığmadığında değişken.max tan tekrar başladığını söyledim.

Önceden değişken türleri ile sık sık hata yaptığım için artık böyle sorunları koda bakar bakmaz görüyorum :-)

Soruda kodun doğru çalışması için ne yapılmasını sormadığı için nasıl yapılacağını söylemedim(Yarın sınavım var koda hiç bir işlem yapmadım. Derlediğimde zaten her şey belli idi. - değer değişkene sığmamış. uint.max yazdırılmış. Yeterince ip ucu vardı :-p ).
erdem (Moderatör) #8
Üye Tem 2009 tarihinden beri · 978 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Sadece daha açık görülsün diye yazdım. Burada değer eksi bir sayı alınca (işaretsiz sayıların sadece pozitif değerler tutabileceğini biliyoruz) sanırım taşma oluyor ve geçersiz bir değer başkabirSayı'ya atanıyor.
    uint ortaDeğer = 3;
 
    uint başkabirDeğer;
 
    double başkabirSayi = başkabirDeğer - ortaDeğer;
 
    writeln(başkabirSayi);
canalpay (Moderatör) #9
Kullanıcı başlığı: Can Alpay Çiftçi
Üye Tem 2009 tarihinden beri · 1133 mesaj · Konum: İzmir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Evet bende aynı şeyi demek istemiştim taşma sözcüğü aklıma gelmedi. Sağol.
erdem:
işaretsiz olarak tanımlarsak bir taşma olduğu için cast(double) ile de kurtaramıyoruz

Ayrıca bende açıkça görülsün diye yazıyorum yoksa dediklerimin bilindiğini biliyorum.

Eğer işlem yaptığımız sayının mutlak değeri, değişkenin alabileceği değer aralığından(Dikkat: değişken maksimum değeri demedim!) fazla değilse ve kullandığımız değişkenlerin türünü(daha doğrusu aralığını) biliyor isek taşmaları kurtarabiliriz.

Örn:

void main()
{
    uint değer = -3;
 
    writeln(değer);
    
    int ideğer=değer;
    
    writeln(ideğer);
    
    
}
Doğrulama Kodu: VeriCode Lütfen resimde gördüğünüz doğrulama kodunu girin:
İfadeler: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Özel Karakterler:
Forum: Ders Arası RSS
Bağlı değilsiniz. · Şifremi unuttum · ÜYELİK
This board is powered by the Unclassified NewsBoard software, 20100516-dev, © 2003-10 by Yves Goergen
Şu an: 2017-11-18, 17:26:55 (UTC -08:00)