D Programlama Dili – Programlama dersleri ve D referansı
Ali Çehreli

arttırma: [increment], değerini bir arttırmak
atama: [assign], değişkene yeni bir değer vermek
azaltma: [decrement], değerini bir azaltmak
bit: [bit], 0 ve 1 değerlerini alabilen en temel bilgi birimi
ifade: [expression], programın değer oluşturan veya yan etki üreten bir bölümü
işaretli tür: [signed type], eksi ve artı değer alabilen tür
işaretsiz tür: [unsigned type], yalnızca artı değer alabilen tür
işleç: [operator], bir veya daha fazla ifadeyle iş yapan özel işaret (+, -, =, [], vs.)
kalan: [modulus], bölme işleminin kalan değeri
kırpılma: [truncate], sayının virgülden sonrasının kaybedilmesi
nitelik: [property, attribute], bir türün veya nesnenin bir özelliği
önceki değerli arttırma: [post-increment], sayıyı arttıran ama önceki değerini üreten işleç
önceki değerli azaltma: [post-decrement], sayıyı azaltan ama önceki değerini üreten işleç
taşma; üstten veya alttan: [overflow veya underflow], değerin bir türe sığamayacak kadar büyük veya küçük olması
... bütün sözlük



İngilizce Kaynaklar


Diğer




Tamsayılar ve Aritmetik İşlemler

D'nin karar verme ile ilgili yapılarından if'i ve while'ı gördük. Bu bölümde temel türlerin sayısal olanlarıyla yapılan aritmetik işlemlere bakacağız. Böylece bundan sonraki bölümlerde çok daha becerikli ve ilginç programlar yazabileceksiniz.

Aritmetik işlemler aslında son derece basittirler çünkü zaten günlük hayatımızda her zaman karşımıza çıkarlar. Buna rağmen, temel türlerle ilgilenirken mutlaka bilinmesi gereken çok önemli kavramlar da vardır. Tür uzunluğu, taşma, ve kırpılma kavramlarını anlıyorsanız bütün konuyu bu tabloya bakarak geçebilirsiniz:

İşleç Etkisi Örnek kullanım
++ değerini bir arttırır ++değişken
-- değerini bir azaltır --değişken
+ iki değerin toplamı birinci + ikinci
- birinciden ikincinin çıkarılmışı birinci - ikinci
* iki değerin çarpımı birinci * ikinci
/ birincinin ikinciye bölümü birinci / ikinci
% birincinin ikinciye bölümününden kalan birinci % ikinci
^^ birincinin ikinci'nin değeri kadar üssü
(birincinin ikinci kere kendisiyle çarpımı)
birinci ^^ ikinci

Tablodaki ikili işleçlerin yanına = karakteri gelenleri de vardır: +=, -=, *=, /=, %=, ve ^^=. Bunlar işlemin sonucunu soldaki değişkene atarlar:

    sayı += 10;

O ifade sayı'ya 10 ekler ve sonucu yine sayı'ya atar; sonuçta değerini 10 arttırmış olur. Şu ifadenin eşdeğeridir:

    sayı = sayı + 10;

Taşma: Her değer her türe sığmaz ve taşabilir. Örneğin, 0 ile 255 arasında değerler tutabilen ubyte'a 260 değeri verilmeye kalkışılırsa değeri 4 olur. (Not: C ve C++ gibi bazı dillerin tersine, taşma D'de işaretli türler için de yasaldır ve işaretsiz türlerle aynı davranışa sahiptir.)

Kırpılma: Tamsayılar virgülden sonrasını tutamazlar. Örneğin 3/2 ifadesinin değeri 1 olur.

Eğer bu kavramları örneğin başka dillerden biliyorsanız, bu kadarı yetebilir. İsterseniz geri kalanını okumayabilirsiniz, ama yine de sondaki problemleri atlamayın.

Bu bölüm ilgisiz bilgiler veriyor gibi gelebilir; çünkü aritmetik işlemler hepimizin günlük hayatta sürekli olarak karşılaştığımız kavramlardır: Tanesi 10 lira olan bir şeyden iki tane alırsak 20 lira veririz, veya üçü 45 lira olan şeylerin tanesi 15 liradır.

Ne yazık ki işler bilgisayarda bu kadar basit olmayabilir. Sayıların bilgisayarda nasıl saklandıklarını bilmezsek, örneğin 3 milyar borcu olan bir firmanın 3 milyar daha borç alması sonucunda borcunun 1.7 milyara düştüğünü görebiliriz. Başka bir örnek olarak, 1 kutusu 4 çocuğa yeten dondurmadan 11 çocuk için 2 tane yetecek diye hesaplayabiliriz.

Bu bölüm size öncekilerden daha teknik gelebilir ama tamsayıların bilgisayarda nasıl ifade edildiklerinin bir programcı tarafından mutlaka bilinmesi gerekir.

Tamsayılar

Tamsayılar ancak tam değerler alabilen türlerdir: -2, 0, 10, vs. Bu türler 2.5 gibi kesirli değerler tutamazlar. Daha önce temel türler tablosunda da gördüğünüz tamsayı türleri şunlardır:


Tür
Bit
Uzunluğu
İlk
Değeri
byte 8 0
ubyte 8 0
short 16 0
ushort 16 0
int 32 0
uint 32 0
long 64 0L
ulong 64 0LU

Hatırlarsanız, tür isimlerinin başındaki u karakteri "unsigned"dan geliyordu ve "işaretsiz" demekti. O türler eksi işareti olmayan türlerdir ve yalnızca sıfır ve daha büyük değerler alabilirler.

Her ne kadar diğer türler gibi 0 değerine eşit olsalar da, 0L ve 0LU sırasıyla long ve ulong türünde hazır değerlerdir.

Bitler ve tür uzunlukları

Günümüzdeki bilgisayar sistemlerinde en küçük bilgi parçası bittir. Bit, elektronik düzeyde ve devrelerin belirli noktalarında elektrik geriliminin var olup olmaması kavramıyla belirlendiği için, ancak iki durumdan birisinde bulunabilir. Bu durumlar 0 ve 1 değerleri olarak kabul edilmişlerdir. Yani sonuçta bir bit, iki değişik değer saklayabilir.

Yalnızca iki durumla ifade edilebilen kavramlarla fazla karşılaşmadığımız için bitin kullanışlılığı da azdır: yazı veya tura, odada ışıkların açık olup olmadığı, vs. gibi iki durumu olan kavramlar...

Biraz ileri giderek iki biti bir araya getirirsek, ikisinin birlikte saklayabilecekleri toplam değer adedi artar. İkisinin ayrı ayrı 0 veya 1 durumunda olmalarına göre toplam 4 olasılık vardır. Soldaki rakam birinci biti, sağdaki rakam da ikinci biti gösteriyor olsun: 00, 01, 10, ve 11. Yani bir bit eklemeyle toplam durum sayısı ikiye katlanmış olur. Bit eklemenin etkisini daha iyi görebilmek için bir adım daha atabiliriz: Üç bit, toplam 8 değişik durumda bulunabilir: 000, 001, 010, 011, 100, 101, 110, 111.

Bu sekiz durumun hangi tamsayı değerlerine karşılık gelecekleri tamamen anlaşmalara ve geleneklere kalmıştır. Yoksa örneğin 000 durumu 42 değerini, 001 durumu 123 değerini, vs. gösteriyor da olabilirdi. Tabii bu kadar ilgisiz değerler kullanışlı olmayacaklarından, 3 bitlik bir türü örnek alırsak, bu 8 durumun işaretli ve işaretsiz olarak kullanılmasında aldığı değerler şu tablodakine benzer:

Bitlerin
Durumu
İşaretsiz
Değer
İşaretli
Değer
000 0 0
001 1 1
010 2 2
011 3 3
100 4 -4
101 5 -3
110 6 -2
111 7 -1

Burada görmenizi istediğim, 3 bitten nasıl 8 farklı değer elde edilebildiğidir.

Görüldüğü gibi, eklenen her bit, saklanabilecek bilgi miktarını iki katına çıkartmaktadır. Bunu devam ettirirsek; bitlerin bir araya getirilmelerinden oluşturulan değişik uzunluktaki türlerin saklayabildikleri farklı değer miktarlarını, bir önceki bit uzunluğunun saklayabileceği değer miktarını 2 ile çarparak şöyle görebiliriz:

Bit
Adedi
Saklanabilecek
Farklı Değer Adedi

D Türü
En Küçük
Değeri
En Büyük
Değeri
12
24
38
416
532
664
7128
8256 byte
ubyte
-128
0
127
255
......
1665,536 short
ushort
-32768
0
32767
65535
......
324,294,967,296 int
uint
-2147483648
0
2147483647
4294967295
......
6418,446,744,073,709,551,616 long
ulong
-9223372036854775808
0
9223372036854775807
18446744073709551615
......

Bazı tablo satırlarını atladım ve aynı sayıda bitten oluşan D türlerinin işaretli ve işaretsiz olanlarını aynı satırda gösterdim (örneğin int ve uint 32 bitlik satırdalar).

Hangi durumda hangi tür

D'de üç bitlik tür yoktur. Olsaydı, tutabildiği 8 farklı değer ile ancak atılan zarın sonucu veya haftanın gün sayısı gibi kavramları ifade etmek için kullanılabilirdi.

Öte yandan, uint çok büyük bir tür olsa da, dünyadaki bütün insanları kapsayacak bir kimlik kartı numarası gibi bir kavram için kullanılamaz, çünkü uint dünyadaki insan nüfusu olan 7 milyardan daha az sayıda değer saklayabilir. long ve ulong'un Türkçe'de nasıl okunduğunu bile bilemeyeceğim toplam değer adedi ise çoğu kavram için fazlasıyla yeterlidir.

Temel bir kural olarak, özel bir neden yoksa, tamsayılar için öncelikle int'i düşünebilirsiniz.

Taşma

Türlerin bit sayılarıyla belirlenen bu kısıtlamaları, onlarla yapılan işlemlerde beklenmedik sonuçlar verebilir. Örneğin değerleri 3 milyar olan iki uint'in toplamı gerçekte 6 milyar olsa da, en fazla 4 milyar kadar değer saklayabilen uint'e sığmaz. Bu durumda sonuç uint'ten taşmış olur; programda hiçbir uyarı verilmeden 6 milyarın ancak 4 milyardan geri kalanı, yani 2 milyar kadarı sonuç değişkeninde kalır. (Aslında 6 milyar eksi 4.3 milyar, yani yaklaşık olarak 1.7 milyar...)

Kırpılma

Tamsayılar kesirli değerler tutamadıkları için ne kadar önemli olsa da, virgülden sonraki bilgiyi kaybederler. Örneğin 1 kutusu 4 çocuğa yeten dondurmadan 11 çocuk için 2.75 kutu gerekiyor olsa bile, bu değer bir tamsayı tür içinde ancak 2 olarak saklanabilir.

Taşmaya ve kırpılmaya karşı alabileceğiniz bazı önlemleri işlemlerin tanıtımından sonra vereceğim. Önce aritmetik işlemleri tanıyalım.

Tür nitelikleri hatırlatması

Temel türlerin tanıtıldığı bölümde tür niteliklerini görmüştük: .min, türün alabileceği en küçük değeri; .max da en büyük değeri veriyordu.

Arttırma: ++

Tek bir değişkenle kullanılır. Değişkenin isminden önce yazılır ve o değişkenin değerini 1 arttırır:

import std.stdio;

void main() {
    int sayı = 10;
    ++sayı;
    writeln("Yeni değeri: ", sayı);
}
Yeni değeri: 11

Arttırma işleci, biraz aşağıda göreceğiniz atamalı toplama işlecinin 1 değeri ile kullanılmasının eşdeğeridir:

    sayı += 1;      // ++sayı ifadesinin aynısı

Arttırma işleminin sonucu; eğer türün taşıyabileceği en yüksek değeri aşıyorsa, o zaman taşar ve türün alabildiği en düşük değere dönüşür. Bunu denemek için önceki değeri int.max olan bir değişkeni arttırırsak, yeni değerinin int.min olduğunu görürüz:

import std.stdio;

void main() {
    writeln("en düşük int değeri   : ", int.min);
    writeln("en yüksek int değeri  : ", int.max);

    int sayı = int.max;
    writeln("sayının önceki değeri : ", sayı);
    ++sayı;
    writeln("sayının sonraki değeri: ", sayı);
}
en düşük int değeri   : -2147483648
en yüksek int değeri  : 2147483647
sayının önceki değeri : 2147483647
sayının sonraki değeri: -2147483648

Bu çok önemli bir konudur; çünkü sayı hiçbir uyarı verilmeden, en yüksek değerinden en düşük değerine geçmektedir; hem de arttırma işlemi sonucunda!

Buna taşma denir. Benzer taşma davranışlarını azaltma, toplama, ve çıkarma işlemlerinde de göreceğiz.

Azaltma: --

Tek bir değişkenle kullanılır. Değişkenin isminden önce yazılır ve o değişkenin değerini 1 azaltır:

    --sayı;   // değeri bir azalır

Azaltma işleci, biraz aşağıda göreceğiniz atamalı çıkarma işlecinin 1 değeri ile kullanılmasının eşdeğeridir:

    sayı -= 1;      // --sayı ifadesinin aynısı

++ işlecine benzer şekilde, eğer değişkenin değeri baştan o türün en düşük değerindeyse, yeni değeri o türün en yüksek değeri olur. Buna da taşma denir.

Toplama: +

İki ifadeyle kullanılır ve aralarına yazıldığı iki ifadenin toplamını verir:

import std.stdio;

void main() {
    int sayı_1 = 12;
    int sayı_2 = 100;

    writeln("Sonuç: ", sayı_1 + sayı_2);
    writeln("Sabit ifadeyle: ", 1000 + sayı_2);
}
Sonuç: 112
Sabit ifadeyle: 1100

Eğer iki ifadenin toplamı o türde saklanabilecek en yüksek değerden fazlaysa, yine taşma oluşur ve değerlerin ikisinden de daha küçük bir sonuç elde edilir:

import std.stdio;

void main() {
    // İki tane 3 milyar
    uint sayı_1 = 3000000000;
    uint sayı_2 = 3000000000;

    writeln("uint'in en yüksek değeri: ", uint.max);
    writeln("                  sayı_1: ", sayı_1);
    writeln("                  sayı_2: ", sayı_2);
    writeln("                  toplam: ", sayı_1 + sayı_2);
    writeln("TAŞMA! Sonuç 6 milyar olmadı!");
}
uint'in en yüksek değeri: 4294967295
                  sayı_1: 3000000000
                  sayı_2: 3000000000
                  toplam: 1705032704
TAŞMA! Sonuç 6 milyar olmadı!
Çıkarma: -

İki ifadeyle kullanılır ve birinci ile ikincinin farkını verir:

import std.stdio;

void main() {
    int sayı_1 = 10;
    int sayı_2 = 20;

    writeln(sayı_1 - sayı_2);
    writeln(sayı_2 - sayı_1);
}
-10
10

Eğer sonucu tutan değişken işaretsizse ve sonuç eksi bir değer alırsa, yine garip sonuçlar doğar. Yukarıdaki programı uint için tekrar yazarsak:

import std.stdio;

void main() {
    uint sayı_1 = 10;
    uint sayı_2 = 20;

    writeln("SORUN! uint eksi değer tutamaz:");
    writeln(sayı_1 - sayı_2);
    writeln(sayı_2 - sayı_1);
}
SORUN! uint eksi değer tutamaz:
4294967286
10

Eninde sonunda farkları alınacak kavramlar için hep işaretli türlerden seçmek iyi bir karardır. Yine, özel bir neden yoksa normalde int'i seçebilirsiniz.

Çarpma: *

İki ifadenin değerlerini çarpar. Yine taşmaya maruzdur:

import std.stdio;

void main() {
    uint sayı_1 = 6;
    uint sayı_2 = 7;

    writeln(sayı_1 * sayı_2);
}
42
Bölme: /

Birinci ifadeyi ikinci ifadeye böler. Tamsayılar kesirli sayı tutamayacakları için, eğer varsa sonucun kesirli kısmı atılır. Buna kırpılma denir. Örneğin bu yüzden aşağıdaki program 3.5 değil, 3 yazmaktadır:

import std.stdio;

void main() {
    writeln(7 / 2);
}
3

Virgülden sonrasının önemli olduğu hesaplarda tamsayı türleri değil, kesirli sayı türleri kullanılır. Kesirli sayı türlerini bir sonraki bölümde göreceğiz.

Kalan: %

Birinci ifadeyi ikinci ifadeye böler ve kalanını verir. Örneğin 10'un 6'ya bölümünden kalan 4'tür:

import std.stdio;

void main() {
    writeln(10 % 6);
}
4

Bu işleç bir sayının tek veya çift olduğunu anlamada kullanılır. Tek sayıların ikiye bölümünden kalan her zaman için 1 olduğundan, kalanın 0 olup olmadığına bakarak sayının tek veya çift olduğu kolayca anlaşılır:

    if ((sayı % 2) == 0) {
        writeln("çift sayı");
    } else {
        writeln("tek sayı");
    }
Üs alma: ^^

Birinci ifadenin ikinci ifade ile belirtilen üssünü alır. Örneğin 3 üssü 4, 3'ün 4 kere kendisiyle çarpımıdır:

import std.stdio;

void main() {
    writeln(3 ^^ 4);
}
81
Atamalı aritmetik işleçleri

Yukarıda gösterilen ve iki ifade alan aritmetik işleçlerin atamalı olanları da vardır. Bunlar işlemi gerçekleştirdikten sonra ek olarak sonucu sol taraftaki değişkene atarlar:

import std.stdio;

void main() {
    int sayı = 10;

    sayı += 20;  // sayı = sayı + 20 ile aynı şey; şimdi 30
    sayı -= 5;   // sayı = sayı - 5  ile aynı şey; şimdi 25
    sayı *= 2;   // sayı = sayı * 2  ile aynı şey; şimdi 50
    sayı /= 3;   // sayı = sayı / 3  ile aynı şey; şimdi 16
    sayı %= 7;   // sayı = sayı % 7  ile aynı şey; şimdi  2
    sayı ^^= 6;  // sayı = sayı ^^ 6 ile aynı şey; şimdi 64

    writeln(sayı);
}
64
Eksi işareti: -

Önüne yazıldığı ifadenin değerini artıysa eksi, eksiyse artı yapar:

import std.stdio;

void main() {
    int sayı_1 = 1;
    int sayı_2 = -2;

    writeln(-sayı_1);
    writeln(-sayı_2);
}
-1
2

Bu işlecin sonucunun türü, değişkenin türü ile aynıdır. uint gibi işaretsiz türler eksi değerler tutamadıkları için, bu işlecin onlarla kullanılması şaşırtıcı sonuçlar doğurabilir:

    uint sayı = 1;
    writeln("eksi işaretlisi: ", -sayı);

-sayı ifadesinin türü de uint'tir ve o yüzden eksi değer alamaz:

eksi işaretlisi: 4294967295
Artı işareti: +

Matematikte sayıların önüne yazılan + işareti gibi bunun da hiçbir etkisi yoktur. İfadenin değeri eksiyse yine eksi, artıysa yine artı kalır:

import std.stdio;

void main() {
    int sayı_1 = 1;
    int sayı_2 = -2;

    writeln(+sayı_1);
    writeln(+sayı_2);
}
1
-2
Önceki değerli (sonek) arttırma: ++

Not: Özel bir nedeni yoksa normal arttırma işlecini kullanmanızı öneririm.

Normal arttırma işlecinden farklı olarak ifadeden sonra yazılır. Yukarıda anlatılan ++ işlecinde olduğu gibi ifadenin değerini bir arttırır, ama içinde geçtiği ifadede önceki değeri olarak kullanılır. Bunun etkisini görmek için normal ++ işleciyle karşılaştıralım:

import std.stdio;

void main() {
    int normal_arttırılan = 1;
    writeln(++normal_arttırılan);           // 2 yazılır
    writeln(normal_arttırılan);             // 2 yazılır

    int önceki_değerli_arttırılan = 1;

    // Değeri arttırılır ama ifadede önceki değeri kullanılır:
    writeln(önceki_değerli_arttırılan++);   // 1 yazılır
    writeln(önceki_değerli_arttırılan);     // 2 yazılır
}
2
2
1
2

Yukarıdaki arttırma işleminin olduğu satır şunun eşdeğeridir:

    int önceki_değeri = önceki_değerli_arttırılan;
    ++önceki_değerli_arttırılan;
    writeln(önceki_değeri);                 // 1 yazılır

Yani bir anlamda, sayı arttırılmıştır, ama içinde bulunduğu ifadede önceki değeri kullanılmıştır.

Önceki değerli (sonek) azaltma: --

Not: Özel bir nedeni yoksa normal azaltma işlecini kullanmanızı öneririm.

Önceki değerli arttırma ++ ile aynı şekilde davranır ama arttırmak yerine azaltır.

İşleç öncelikleri

Yukarıdaki işleçleri hep tek başlarına ve bir veya iki ifade ile gördük. Oysa mantıksal ifadelerde olduğu gibi, birden fazla aritmetik işleci bir arada kullanarak daha karmaşık işlemler oluşturabiliriz:

int sayı = 77;
int sonuç = (((sayı + 8) * 3) / (sayı - 1)) % 5;

Mantıksal ifadelerde olduğu gibi, bu işleçlerin de D tarafından belirlenmiş olan öncelikleri vardır. Örneğin, * işlecinin önceliği + işlecininkinden yüksek olduğundan, parantezler kullanılmadığında sayı + 8 * 3 ifadesi önce * işlemi uygulanacağı için sayı + 24 olarak hesaplanır. Bu da yukarıdakinden farklı bir işlemdir.

O yüzden, parantezler kullanarak hem işlemleri doğru sırada uygulatmış olursunuz, hem de kodu okuyan kişilere kodu anlamalarında yardımcı olmuş olursunuz.

İşleç öncelikleri tablosunu ilerideki bir bölümde göreceğiz.

Taşma olduğunu belirlemek

Her ne kadar henüz görmediğimiz işlev ve ref parametre olanaklarını kullansa da, taşma durumunu bildirebilen core.checkedint modülünü burada tanıtmak istiyorum. Bu modül + ve - gibi işleçleri değil, şu işlevleri kullanır: işaretli ve işaretsiz toplama için adds ve addu, işaretli ve işaretsiz çıkarma için subs ve subu, işaretli ve işaretsiz çarpma için muls ve mulu, ve ters işaretlisini almak için negs.

Örneğin, a ve b'nin int türünde iki değişken olduklarını varsayarsak, toplamlarının taşıp taşmadığını aşağıdaki kod ile denetleyebiliriz:

import core.checkedint;

void main() {
    // Deneme amacıyla taşmaya neden oluyoruz
    int a = int.max - 1;
    int b = 2;

    // Aşağıdaki 'adds' işlevi sırasında sonuç taşmışsa bu
    // değişkenin değeri 'true' olur:
    bool taştı_mı = false;
    int sonuç = adds(a, b, taştı_mı);

    if (taştı_mı) {
        // 'sonuç'u kullanamayız çünkü taşmış
        // ...

    } else {
        // 'sonuç'u kullanabiliriz
        // ...
    }
}

Taşma gibi sorunlara karşı etkili bir başka modül, Checked türünü tanımlayan std.experimental.checkedint modülüdür. Ancak, hem kullanımı hem de gerçekleştirmesi kitabın bu noktasında fazla ileri düzey kabul edilmelidir.

Taşmaya karşı önlemler

Eğer bir işlemin sonucu seçilen türe sığmıyorsa, zaten yapılacak bir şey yoktur. Ama bazen sonuç sığacak olsa da ara işlemler sırasında oluşabilecek taşmalar nedeniyle yanlış sonuçlar elde edilebilir.

Bir örneğe bakalım: kenarları 40'a 60 kilometre olan bir alanın her 1000 metre karesine bir elma ağacı dikmek istiyoruz. Kaç ağaç gerekir?

Bu problemi kağıt kalemle çözünce sonucun 40000 çarpı 60000 bölü 1000 olarak 2.4 milyon olduğunu görürüz. Bunu hesaplayan bir programa bakalım:

import std.stdio;

void main() {
    int en  = 40000;
    int boy = 60000;
    int ağaç_başına_yer = 1000;

    int gereken_ağaç = en * boy / ağaç_başına_yer;

    writeln("Gereken elma ağacı: ", gereken_ağaç);
}
Gereken elma ağacı: -1894967

Bırakın yakın olmayı, bu sonuç sıfırdan bile küçüktür! Bu sorunun nedeni, programdaki en * boy alt işleminin bir int'e sığamayacak kadar büyük olduğu için taşması, ve bu yüzden de geri kalan / ağaç_başına_yer işleminin de yanlış çıkmasıdır.

Buradaki ara işlem sırasında oluşan taşmayı değişken sıralarını değiştirerek giderebiliriz:

int gereken_ağaç = en / ağaç_başına_yer * boy;

Şimdi hesap doğru çıkar:

Gereken elma ağacı: 2400000

Bu ifadenin doğru çalışmasının nedeni, şimdiki ara işlem olan en / ağaç_başına_yer ifadesinin değerinin 40 olduğu için artık int'ten taşmamasıdır.

Aslında böyle bir durumda en doğru çözüm; bir tamsayı türü değil, kesirli sayı türlerinden birisini kullanmaktır: float, double, veya real.

Kırpılmaya karşı önlemler

Benzer şekilde, ara işlemlerin sırasını değiştirerek kırpılmanın da etkisini azaltabiliriz. Bunun ilginç bir örneğini, aynı sayıya bölüp yine aynı sayıyla çarptığımızda görebiliriz: 10/9*9 işleminin sonucunun 10 çıkmasını bekleriz. Oysa:

import std.stdio;

void main() {
    writeln(10 / 9 * 9);
}
9

Yine, işlemlerin sırasını değiştirince kırpılma olmayacağı için sonuç doğru çıkar:

writeln(10 * 9 / 9);
10

Burada da en iyi çözüm belki de bir kesirli sayı türü kullanmaktır.

Problemler
  1. Yazacağınız program kullanıcıdan iki tamsayı alsın ve birincinin içinde ikinciden kaç tane bulunduğunu ve artanını versin. Örneğin 7 ve 3 değerleri girilince çıkışa şunu yazsın:
    7 = 3 * 2 + 1
    
  2. Aynı programı, kalan 0 olduğunda daha kısa sonuç verecek şekilde değiştirin. Örneğin 10 ve 5 verince gereksizce "10 = 5 * 2 + 0" yazmak yerine, yalnızca yeterli bilgiyi versin:
    10 = 5 * 2
    
  3. Dört işlemi destekleyen basit bir hesap makinesi yazın. İşlemi bir menüden seçtirsin ve girilen iki değere o işlemi uygulasın. Bu programda taşma ve kırpılma sorunlarını gözardı edebilirsiniz.
  4. Yazacağınız program 1'den 10'a kadar bütün sayıları ayrı satırlarda olacak şekilde yazdırsın. Ama, bir istisna olarak 7 değerini yazdırmasın. Programda şu şekilde tekrarlanan writeln ifadeleri kullanmayın:
    import std.stdio;
    
    void main() {
        // Böyle yapmayın!
        writeln(1);
        writeln(2);
        writeln(3);
        writeln(4);
        writeln(5);
        writeln(6);
        writeln(8);
        writeln(9);
        writeln(10);
    }
    

    Onun yerine, bir döngü içinde değeri arttırılan bir değişken düşünün ve 7'yi yazdırmama koşuluna da dikkat edin. Burada herhalde eşit olmama koşulunu denetleyen != işlecini kullanmak zorunda kalacaksınız.