Makale Özeti

Bir çoğumuz uygulamalarımızda Math.Round fonksiyonunu kullanırız. Peki ama nasıl çalıştığını, nasıl çalıştığından da önemlisi ilginç ve de özellikle finansal uygulamalarda problem çıkarabilecek yapısını inceleyeceleyelim

Makale

    Merhabalar, Bir çoğumuz uygulamalarımızda Math.Round fonksiyonunu kullanırız. Peki ama nasıl çalıştığını, nasıl çalıştığından da önemlisi ilginç ve de özellikle finansal uygulamalarda problem çıkarabilecek yapısını inceleyeceleyelim.

    Öncelikle senaryoya bakalım. Bir taşımacılık firmasının küçük hacim değerlerinden çok büyük hacim değer ve birimlerine kadar hesap yaptığını ve fiyatlandırmaların da bu hacim değerleri aralığında değiştiği bir durumda, bazen cm3 ile fiyat hesaplamanız gerekirken bazen m3'ler ile uğraşmanız gerekecektir. Böyle bir durumda gerek hacim tutarları gerekse fiyatlar bazında sık sık rounding işlemi yapmanız gerekecektir.

    Peki, rounding (yuvarlama) nasıl çalışır ve bize ne gibi bir sorun çıkarabilir?

    calışma mantığı şu şekildedir.

  •         round edilerek atılacak olan kısım 0.nnnn50000 den küçük ise aşağı (nnnn round edilmeyecek olan kısımlar)
  •         round edilerek atılacak olan kısım 0.nnnn50000 den büyük ise yukarı
  •         eğer 0.nnnn5000 e eşit ise PROBLEM BURADA ÇIKIYOR. bu durumda, round edilecek digit'in bir soluna bakılır ve eğer bu sol digit çift ise aşağı tek ise yukarı yuvarlanır.

        yani :

             MessageBox.Show(Math.Round(12.325, 2))
             MessageBox.Show(Math.Round(12.315, 2))

 bu iki satırın da çalışması sonucu 12.32 çıkacaktır.

   Peki bu sorun mu? ya da başka bir deyişle büyük bir sorun mu? Malesef bir çok finansal uygulamada evet. Çünkü bu gibi tam orta nokta olan değerler için kabul edilmiş daha basit bir kural vardir. Aşağı yuvarlanacak ve ya yukarı yuvarlanacak. Dolayısıyla Math.Round fonksiyonu tam işimizi görmemekte.

    Üstelik bu fonksiyon, access queryleri, vb scriptler, vb 6.0 gibi bir çok uygulamada da bu şekilde çalışmakta. Peki öyleyse ne yapmalıyız. Elbette kendi round fonksiyonumuzu yazacağız. Örneğin aşağıdaki kod bu durumdaki sayıları yukarıya yuvarlayan bir fonksiyonu göstermekte. (bilgi: TL-YTL geçişlerinde de bu durumda olan fiyatlar, tutarlar yukarı yuvarlanarak geçiş sağlanmıştı.)

            public double xRound(double number,int digits)
            {
 
                  System.Threading.Thread.CurrentThread.CurrentCulture=new  System.Globalization.CultureInfo("tr-TR");
 
                  string Temp;
                  string Temp2;
                  int i, j;
                  double Sonuc;
                  double nTemp;
 
                  Temp = Convert.ToString(number);
                 
                  i = Temp.LastIndexOf(",");
 
                  if (((Temp.Length - (i + 1)) <= digits) || (i == -1))
 
                        return number;
 
                  Temp2 = Temp.Substring(i + digits + 1, 1);
                  j = Convert.ToInt32(Temp2);
                  nTemp = Convert.ToDouble(Temp.Substring(0, i + digits + 1));
 
                  if (j == 5)
                        Sonuc = nTemp + (1 / (Math.Pow(10,digits)));
                  else
                        Sonuc = Math.Round(number, digits);
                  return Sonuc;
 
            }
 

 Yukarıda yazdığımız xRound fonksiyonu sayesinde 12.325 değerini 12.33 e, 12.315 değerini ise 12.32 ye yuvarlıyoruz. Bu da finansal olarak doğru bir sonuc elde etmemimiz sağlıyor.

   Peki birde tersinden gidelim. Örnegin Round fonksiyonu SQL serverda yukarıda bahsedildiği gibi çalışmıyor. Yani zaten istediğimiz, finansal olarak doğru kabul ettiğimiz yapıda geliyor. Bunu tersine çevirmek istersek ne yapacağız? Aşağıdaki kod da SQL server da kendinden önceki digitin tek olup olmadığına bakarak yapılan yuvarlamayı göstermekte.

CREATE  function dbo.xRound(@val as decimal(24,18),@sens as int)
returns decimal(24,18)
as
begin
 
declare @retval as decimal(24,18)
,@temp as decimal(32,18)
 
 
set @temp = @val * power(10,@sens)
 
declare @noktadanSonra as varchar(100)
set @NoktadanSonra = convert(varchar,@temp)
 
set @NoktadanSonra = substring(@NoktadanSonra, charindex('.', @NoktadanSonra),18)
set @temp = convert(decimal(32,18),@NoktadanSonra)
 
 
set @retval =
 case when  @temp = 0.5 then
 case
 when cast(@val * power(10,@sens)as  int) % 2 = 1
     then ceiling (@val* power(10,@sens)) /power(10,@sens)
     else floor(@val * power(10,@sens)) /power(10,@sens)
 end
 else
  round(@val,@sens)
end
 
return @retval
 
end
 

    Bu sayede,  farklı ortamlar  arasındaki rounding farklılıkları ve dolayısıyla Grand Totallere yansıyan büyük farklılıklardan tamamen kurtulabiliriz.

    Umarım bu kadar detaylı rounding işlemleri ile fazla karşılaşmazsınız.

    İyi çalışmalar.

    Kivanc OZUOLMEZ