Hepimiz okulda kayan nokta sayılarını öğrendik. Ondalık noktası olmayan tam sayıların aksine.
Beş sayısının bir tam sayı, 5,43'ün ise kayan noktalı bir sayı olduğunu bilmek için matematik dehası olmanıza gerek yok. Açıkçası, hepimiz ikisi arasında net bir ayrım noktası olan ondalık sayıya aşinayız.
Elbette, kayan noktalı değişkenlerin programlama dillerinde kullanılması ve hatta günlük hesaplamalar için ne kadar gerekli olduğundan ayrı bir ilkel veri türü olarak sınıflandırılması şaşırtıcı değildir.
Python veya Java öğrenmeye başladığınızda float veri türünü kullanacaksınız. Özellikle, iki kayan noktalı sayı arasında bölme işlemini gerçekleştirdiğinizde.
Python'da, kayan noktalı bir sayı bildirmeniz gerekmese bile, bu sayıların ikisini bölmek, aşağıda gösterildiği gibi aynı veri türünde bir sayıyla sonuçlanabilir:
Kullanılan tüm değişkenler için 'float' ilkel veri türünü açıkça bildirmemiz gerekse bile, aynı sonucu Java'da da üretebiliriz.
Anlaşılabileceği gibi, bu tür bir hesaplama bir dizi uygulama için gereklidir ve kayan nokta sayılarıyla yapılan işlemlerin her iki programlama dilinde de bir özellik olarak mevcut olmasının nedeni tam olarak budur.
Oldukça farklı bir şekilde, eğer Solidity'yi öğreniyorsanız, onun kayan noktalı sayı veri tipini içermediğini fark etmiş olmalısınız.
Bunun yerine, eğer Solidity'de herhangi bir kod yazdıysanız, imzalı veya imzasız tamsayı tipindeki sayıları kullanıyor olacaksınız.
Dolayısıyla, önceki bölümde gösterildiği gibi aynı hesaplamayı (beş bölü ikiye) yapmak istiyorsanız, akıllı sözleşmenin bir parçası olarak şu şekilde görünecektir:
Ancak Solidity, float veri tipini desteklemediğinden elde ettiğiniz sonuç aynı olmayacaktır. En azından henüz değil.
Özellikle, Sağlamlık sonucu sıfıra doğru yuvarlayacaktır. Bu durumda ve yukarıda gösterildiği gibi, iki değeri elde edilecektir. Bunu her iki sayının modülo işlemi olarak düşünün.
Normal şartlarda bu çok önemli olmasa da, sonucun hesaplamada hataya yol açabileceği zamanlar vardır. Bu nedenle bölme işleminden mümkün olduğunca kaçınılması veya ertelenmesi tavsiye edilir.
Okuldaki BODMAS kuralı tüm matematik öğrencilerinin çarpmadan önce bölme hesaplamasını yapmasını gerektirse bile, Solidity'de tamsayı bölme işlemi yapmanız gerekiyorsa bu önerilmez.
Üç sayıyla çarpma ve bölme işleminin neden yapıldığını bu basit örnekle öğrenelim:
Akıllı sözleşmeyi dağıtırken bir, üç ve beş rakamlarını girerseniz getResult ve getResult2 işlevleri için aynı değeri elde etmelisiniz, değil mi?
Basit bir hesap makinesi kullanırsanız, kayan nokta değerlerinin yokluğu sayesinde, Katılık'ta bire dönüşen 1,666 kayan nokta değerini elde etmelisiniz.
Ne yazık ki, aşağıda gösterildiği gibi getResult ve getResult2 işlevlerinin sonuçlarını kontrol ettiğinizde olan şey bu değildir:
Önce bölme işlemini yaparsak nihai sonuç sıfır olur. Beklenen bir değerinin aksine, getResult işlevinde bu işlemi sonuna kadar ertelediğinizde.
Anlayabileceğiniz gibi, bu değeri kayan değerlerin yokluğunu tahmin ederek hesaplamış olsak bile, yine de hassasiyet kaybına neden olabilecek bir hata ortaya çıkıyor. Bu da kolaylıkla atlatılabilecek bir ekonomik kayıp anlamına gelebilir.
Peki tamsayı bölme hatasını nasıl önleyebiliriz? Daha da önemlisi, hesaplamalarımızın hassasiyetini nasıl artırırız? Bunu yapmanın en yaygın üç yolunu bulalım.
Bu hatayı aşmak için çeşitli yaklaşımlar olduğu göz önüne alındığında, en basit düzeltmeyle başlayalım ve bir gün daha uğraşmadan önce birkaç yaklaşıma daha bakalım.
Yöntem 1: Çarpan kullanın
Şimdi, bölme işlemini en sona koymanın yanı sıra, hatalarla veya kesin olmayan değerlerle karşılaşmamanızı sağlamanın bir yolu da çarpan kullanmaktır. Aşağıdaki örnekte, daha önce kullandığımız üç sayıyla birlikte 100 çarpanını kullanacağız.
Şimdi, sözleşmeyi aşağıdaki kodla dağıttığınızda ve her iki işlevi de çağırdığınızda çıktı şu olur:
İstenilen çıktı 1.666 veya 166/100 olduğundan çarpan üç sayı ile birlikte çalıştığında getResult2 değerinin bize gerekli doğruluğu sağladığını görebiliriz. Tabii çarpanı getResult fonksiyonundaki gibi kullanmazsanız 1 elde edersiniz.
Solidity'yi kullandığınızda varsayılan olarak beklendiği gibi 0,666'nın sonuçtan kesildiği yer. Yani bu değeri elde etmek için tek yapmanız gereken sonucu çarpana bölmek.
Bildiğiniz gibi, hem imzalı hem de imzasız tamsayılar durumunda yuvarlama söz konusu olduğunda Solidity sıfıra doğru hareket eder; bu nedenle, aşağıdaki kodu eksi bir argümanla dağıtıp çalıştırdığınızda, bu düzeltme işaretli tamsayılar durumunda da işe yarar. , üç ve beş:
İşaretli tamsayılar için işlevler tarafından oluşturulan değerlere gelince, bunlar şunlardır:
Açıkçası, bir çarpan kullanarak işaretli tamsayılar için de kesinliği koruyabiliyoruz. Ancak daha sonra bakacağımız işaretli tam sayıları yuvarlamanın kesin bir yolu var.
Yöntem #2: İmzalı tamsayılar için taban bölümünü kullanın
Şimdi aşağıdaki sayı doğrusuna bakıldığında iki işaretsiz tam sayı arasında tamsayı bölme işleminin sonucu sıfıra yakın yuvarlanır. 1.666 elde edilmesi durumunda olduğu gibi, Solidity bunu daha küçük bir sayı olan 1'e yuvarlar.
Ancak işaretli tamsayılar söz konusu olduğunda -1,6666 sonucu iki sayıdan büyük olan -1'e yuvarlanacaktır. Bu nedenle, Solidity'de varsayılan olarak uygulanan sıfıra yuvarlatılmış bölmenin aksine burada taban bölmesi uygulanmalıdır. Tabii ki kesinlik adına.
Float veri türü mevcut olsaydı -1,666 değeri hesaplanır. Solidity bunu -1'e yuvarlarken, imzalı tam sayılara taban bölümü uygulamak bunu -2'ye düşürecektir.
getResult ve getResult2 işlevlerini çağırdığınızda eksi bir, üç ve beş argümanı için aşağıda gösterilen değeri elde ederiz:
Gördüğünüz gibi, getResult değeri sıfıra yuvarlama yaklaşımını kullanarak hesaplarken, getResult2 bunu kat bölümü sayesinde buradaki en küçük tam sayıya yuvarlar.
Yöntem #3: ABDKMath64x64 kitaplığını kullanın
Şimdi son yöntem olarak bölme işlemlerinin sonucunu sabit nokta sayılarına dönüştüren ABDKMath64x64 kütüphanesini kullanacağız.
Yine, bu kütüphaneyi kullanmanın, Solidity'de varsayılan olarak mevcut olan sıfıra doğru yuvarlama yönteminin aksine doğruluğu arttırdığı söyleniyor. Çıktıyı anlayabilmek için add işleminin sonuçlarını aşağıda gösterildiği gibi div fonksiyonuyla karşılaştıralım:
Akıllı sözleşmeyi dağıtırken 1, 1 ve 1 argümanlarını eklediğinizde aşağıda gösterilen değerleri elde edersiniz:
Ekleme fonksiyonunun iki tamsayı değerini döndürmesi ve üç argümanın toplamının bu kadar olması sürpriz olmamalıdır. Div işlevine gelince, imzalı 64,64 bitlik sabit nokta sayısını temsil eden ve olağan sayı işlemlerini gerçekleştirebileceğiniz bir int 128 değeri döndürülür.
Elbette ABDKMath64x64 kütüphanesi, tamsayı bölme hatasını önlemenin yanı sıra doğruluğu artırmak için kullanılabilecek tek kütüphane değildir.
Fixidity , DSMath ve farklı sayı formatlarını kullanan BANKEX kütüphaneleri gibi birkaç örnekle birlikte birkaç tane daha vardır. Yukarıdaki örnekte kullanılan 64,64 bit sabit noktalı sayı formatından farklı sayı formatları. Bu nedenle, bu kitaplıkları keşfetmek yararlı görünse de, sayı biçimlerinin mevcut diğer kitaplıkların hiçbirinde çalışmayacağını lütfen unutmayın.