Bu makalede Rust akıllı sözleşmeler programlamasındaki sayısal hesaplama sorunları ele alınacaktır. Bu, kayan nokta işlemlerinin hassasiyeti, tam sayı hesaplamalarının hassasiyeti ve sayısal hesaplama için Rust akıllı sözleşmelerinin nasıl yazılacağı gibi konuları içermektedir.
1. Kesirli sayıların hesaplama hassasiyeti problemi
Rust dilinin yerel olarak ondalık sayı işlemlerini desteklemesine rağmen, ondalık sayı işlemlerinin kaçınılmaz hesaplama hassasiyeti sorunları vardır. Akıllı sözleşmeler yazarken, ondalık sayı işlemleri kullanılması önerilmez, özellikle de önemli ekonomik/finansal kararlar içeren oranlar veya faiz oranlarıyla ilgilenirken.
Rust dilindeki çift hassasiyetli kayan nokta türü f64, IEEE 754 standardına uyar ve 2 tabanlı bilimsel notasyon kullanır. Ancak, bazı ondalık sayılar ( gibi 0.7), sonlu uzunluktaki kayan noktalarda doğru bir şekilde temsil edilemez ve "yuvarlama" olayıyla karşılaşılır.
NEAR kamu zincirinde on kullanıcıya 0.7 NEAR token dağıtıldığında, gerçek hesaplama sonuçlarında tam olmayan durumlar ortaya çıkabilir:
pas
#[test]
fn precision_test_float() {
let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
println!("Miktarın değeri: {:.20}", amount);
assert_eq!(result_0, 0.07, "");
}
Yürütme sonucu, amount değerinin tam olarak 0.7 değil, son derece yakın bir değer olan 0.69999999999999995559 olduğunu gösteriyor. Ayrıca, daha ileri bölme işleminin sonucu da beklenen 0.07 yerine, tam olmayan 0.06999999999999999 haline geliyor.
Bu sorunu çözmek için, sabit noktayı kullanmayı düşünebilirsiniz. NEAR Protocol'de, genellikle 10^24 payda olarak kullanılır, yani 10^24 yoctoNEAR, 1 NEAR token ile eşdeğerdir.
Düzeltilmiş test kodu aşağıdaki gibidir:
pas
#[test]
fn precision_test_integer() {
let N: u128 = 1_000_000_000_000_000_000_000_000;
let amount: u128 = 700_000_000_000_000_000_000_000;
let divisor: u128 = 10;
let result_0 = amount / divisor;
assert_eq!(result_0, 70_000_000_000_000_000_000_000, "");
}
Bu şekilde doğru hesaplama sonuçları elde edilebilir: 0.7 NEAR / 10 = 0.07 NEAR.
2. Rust tam sayılarının hesaplama hassasiyeti sorunu
Tam sayılarla yapılan hesaplamaların bazı durumlarda kayan nokta hassasiyeti kaybı sorununu çözebilmesine rağmen, tam sayı hesaplamalarının sonuçları da tamamen doğru ve güvenilir değildir. Tam sayı hesaplama hassasiyetini etkileyen bazı nedenler şunlardır:
2.1 İşlem Sırası
Aynı aritmetik önceliğe sahip çarpma ve bölme işlemlerinin sırasının değişmesi, hesaplama sonucunu doğrudan etkileyebilir. Örneğin:
pas
#[test]
fn precision_test_div_before_mul() {
let a: u128 = 1_0000;
let b: u128 = 10_0000;
let c: u128 = 20;
let result_0 = a.checked_mul(c).expect("ERR_MUL")
.checked_div(b).expect("ERR_DIV");
let result_1 = a.checked_div(b).expect("ERR_DIV")
.checked_mul(c).expect("ERR_MUL");
assert_eq!(result_0,result_1,"");
}
Test sonuçları, result_0 ve result_1'in hesaplama sonuçlarının farklı olduğunu gösteriyor. Bunun nedeni, tam sayı bölme için, paydayı aşan hassasiyetin kaybolmasıdır. result_1'i hesaplarken, (a / b) öncelikle hesaplama hassasiyetini kaybedecek ve 0'a dönüşecek; oysa result_0'ı hesaplarken, a * c'yi öncelikle hesaplamak hassasiyet kaybını önleyebilir.
2.2 çok küçük bir ölçek
Küçük ölçekli hesaplamalar söz konusu olduğunda, hassasiyet sorunları da ortaya çıkabilir:
pas
#[test]
fn precision_test_decimals() {
let a: u128 = 10;
let b: u128 = 3;
let c: u128 = 4;
let decimal: u128 = 100_0000;
let result_0 = a.checked_div(b).expect("ERR_DIV")
.checked_mul(c).expect("ERR_MUL");
let result_1 = a.checked_mul(decimal).expect("ERR_MUL")
.checked_div(b).expect("ERR_DIV")
.checked_mul(c).expect("ERR_MUL")
.checked_div(decimal).expect("ERR_DIV");
println!("{}:{}", result_0, result_1);
assert_eq!(result_0, result_1, "");
}
Test sonuçları, result_0 ve result_1'in işlem sonuçlarının farklı olduğunu ve result_1 = 13'ün gerçek beklenen hesap değeri 13.3333....'e daha yakın olduğunu göstermektedir.
3. Rust akıllı sözleşmelerinde sayısal actuarial nasıl yazılır
Kesinliği artırmak için aşağıdaki koruma önlemleri alınabilir:
3.1 İşlem sırasını ayarlama
Tam sayı çarpımının tam sayı bölümünden öncelikli olmasını sağlayın.
3.2 Tam sayıların büyüklüğünü artırma
Daha büyük bir büyüklük kullanarak, daha büyük moleküller yaratın. Örneğin, 5.123 NEAR'ı 5.123 * 10^10 = 51_230_000_000 olarak ifade edebilirsiniz.
3.3 Birikim işlemleri doğruluk kaybı
Kaçınılmaz tam sayı hesaplama hassasiyeti sorunları için, birikmiş hesaplama hassasiyeti kaybını kaydetmeyi düşünebilirsiniz. Örneğin:
pas
const USER_NUM: u128 = 3;
u128 {
let dağıtılacak_token = offset + amount;
let per_user_share = token_to_distribute / USER_NUM;
println!("per_user_share {}", per_user_share);
let recorded_offset = token_to_distribute - per_user_share * USER_NUM;
kayıtlı_ofset
}
#(
fn record_offset_test)[test] {
let mut offset: u128 = 0;
for i in 1..7 {
println!("Round {}", i);
offset = distribute(to_yocto)"10"(, offset(;
println!)"Offset {}\n", offset);
}
}
Bu yöntem, hassasiyet kaybı nedeniyle dağıtılmamış jetonları biriktirip yeniden dağıtabilir.
Bu kütüphane, etkili hassasiyet hesaplamaları ve yuvarlama hatası olmayan ondalık finansal hesaplamalar için uygundur.
) 3.5 Yuvarlama mekanizmasını dikkate alarak
Akıllı sözleşmeler tasarlanırken, yuvarlama sorunları genellikle "Ben avantaj sağlamak istiyorum, başkaları benim avantajımdan yararlanamaz" ilkesine göre hareket eder. Bu ilkeye göre, eğer aşağı yuvarlama sözleşmeye faydalıysa, aşağı; eğer yukarı yuvarlama sözleşmeye faydalıysa, yukarı; beşli yuvarlama, kimin lehine olduğunu belirleyemediği için nadiren kullanılır.
Bu yöntemleri kullanarak, Rust akıllı sözleşmelerinde daha hassas sayısal hesaplamalar gerçekleştirilebilir, sözleşmenin güvenilirliğini ve adilliğini artırabilir.
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
14 Likes
Reward
14
8
Share
Comment
0/400
ZeroRushCaptain
· 07-17 09:17
Ah, yine bu şeyi kurcalamaya başladılar. Geçen sefer kilitli pozisyonu nedeniyle hassasiyet sorunuyla geri döndüm, toprak yemeye kadar kaybettim.
View OriginalReply0
BlockchainFries
· 07-16 10:52
Float sayısı bu işi çok berbat ediyor, kim kullanırsa başı belaya girer.
View OriginalReply0
MoonBoi42
· 07-15 08:48
Kayan nokta hassasiyeti nerede sabit nokta keyfi~
View OriginalReply0
OnchainDetective
· 07-14 09:54
Açıkça görülüyor ki, hassasiyet hatası bazı cross-chain köprüleri hackerlar tarafından istismar edilen bir zayıflık oluşturuyor. Korkunç.
View OriginalReply0
HashRateHermit
· 07-14 09:54
Kesinlik sorunu beni gerçekten mahvetti.
View OriginalReply0
GateUser-e87b21ee
· 07-14 09:47
Yine ondalık noktalarla ilgili işler çıktı, kafam büyüdü.
View OriginalReply0
BearMarketSurvivor
· 07-14 09:45
Kesinlikten kimse kaçamaz, bu konuyu bilen bu kişi.
View OriginalReply0
AirdropLicker
· 07-14 09:35
Kayan nokta sayısı yuvarlama bu çukur çok derin, daha önce düştüm.
Rust akıllı sözleşmelerindeki sayısal hesaplama teknikleri ve dikkat edilmesi gereken hususlar
Rust akıllı sözleşmeler yetiştirme günlüğü (7): Sayısal hesaplama
Bu makalede Rust akıllı sözleşmeler programlamasındaki sayısal hesaplama sorunları ele alınacaktır. Bu, kayan nokta işlemlerinin hassasiyeti, tam sayı hesaplamalarının hassasiyeti ve sayısal hesaplama için Rust akıllı sözleşmelerinin nasıl yazılacağı gibi konuları içermektedir.
1. Kesirli sayıların hesaplama hassasiyeti problemi
Rust dilinin yerel olarak ondalık sayı işlemlerini desteklemesine rağmen, ondalık sayı işlemlerinin kaçınılmaz hesaplama hassasiyeti sorunları vardır. Akıllı sözleşmeler yazarken, ondalık sayı işlemleri kullanılması önerilmez, özellikle de önemli ekonomik/finansal kararlar içeren oranlar veya faiz oranlarıyla ilgilenirken.
Rust dilindeki çift hassasiyetli kayan nokta türü f64, IEEE 754 standardına uyar ve 2 tabanlı bilimsel notasyon kullanır. Ancak, bazı ondalık sayılar ( gibi 0.7), sonlu uzunluktaki kayan noktalarda doğru bir şekilde temsil edilemez ve "yuvarlama" olayıyla karşılaşılır.
NEAR kamu zincirinde on kullanıcıya 0.7 NEAR token dağıtıldığında, gerçek hesaplama sonuçlarında tam olmayan durumlar ortaya çıkabilir:
pas #[test] fn precision_test_float() { let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
println!("Miktarın değeri: {:.20}", amount); assert_eq!(result_0, 0.07, ""); }
Yürütme sonucu, amount değerinin tam olarak 0.7 değil, son derece yakın bir değer olan 0.69999999999999995559 olduğunu gösteriyor. Ayrıca, daha ileri bölme işleminin sonucu da beklenen 0.07 yerine, tam olmayan 0.06999999999999999 haline geliyor.
Bu sorunu çözmek için, sabit noktayı kullanmayı düşünebilirsiniz. NEAR Protocol'de, genellikle 10^24 payda olarak kullanılır, yani 10^24 yoctoNEAR, 1 NEAR token ile eşdeğerdir.
Düzeltilmiş test kodu aşağıdaki gibidir:
pas #[test] fn precision_test_integer() { let N: u128 = 1_000_000_000_000_000_000_000_000;
let amount: u128 = 700_000_000_000_000_000_000_000; let divisor: u128 = 10;
let result_0 = amount / divisor; assert_eq!(result_0, 70_000_000_000_000_000_000_000, ""); }
Bu şekilde doğru hesaplama sonuçları elde edilebilir: 0.7 NEAR / 10 = 0.07 NEAR.
2. Rust tam sayılarının hesaplama hassasiyeti sorunu
Tam sayılarla yapılan hesaplamaların bazı durumlarda kayan nokta hassasiyeti kaybı sorununu çözebilmesine rağmen, tam sayı hesaplamalarının sonuçları da tamamen doğru ve güvenilir değildir. Tam sayı hesaplama hassasiyetini etkileyen bazı nedenler şunlardır:
2.1 İşlem Sırası
Aynı aritmetik önceliğe sahip çarpma ve bölme işlemlerinin sırasının değişmesi, hesaplama sonucunu doğrudan etkileyebilir. Örneğin:
pas #[test] fn precision_test_div_before_mul() { let a: u128 = 1_0000; let b: u128 = 10_0000; let c: u128 = 20;
}
Test sonuçları, result_0 ve result_1'in hesaplama sonuçlarının farklı olduğunu gösteriyor. Bunun nedeni, tam sayı bölme için, paydayı aşan hassasiyetin kaybolmasıdır. result_1'i hesaplarken, (a / b) öncelikle hesaplama hassasiyetini kaybedecek ve 0'a dönüşecek; oysa result_0'ı hesaplarken, a * c'yi öncelikle hesaplamak hassasiyet kaybını önleyebilir.
2.2 çok küçük bir ölçek
Küçük ölçekli hesaplamalar söz konusu olduğunda, hassasiyet sorunları da ortaya çıkabilir:
pas #[test] fn precision_test_decimals() { let a: u128 = 10; let b: u128 = 3; let c: u128 = 4; let decimal: u128 = 100_0000;
}
Test sonuçları, result_0 ve result_1'in işlem sonuçlarının farklı olduğunu ve result_1 = 13'ün gerçek beklenen hesap değeri 13.3333....'e daha yakın olduğunu göstermektedir.
3. Rust akıllı sözleşmelerinde sayısal actuarial nasıl yazılır
Kesinliği artırmak için aşağıdaki koruma önlemleri alınabilir:
3.1 İşlem sırasını ayarlama
Tam sayı çarpımının tam sayı bölümünden öncelikli olmasını sağlayın.
3.2 Tam sayıların büyüklüğünü artırma
Daha büyük bir büyüklük kullanarak, daha büyük moleküller yaratın. Örneğin, 5.123 NEAR'ı 5.123 * 10^10 = 51_230_000_000 olarak ifade edebilirsiniz.
3.3 Birikim işlemleri doğruluk kaybı
Kaçınılmaz tam sayı hesaplama hassasiyeti sorunları için, birikmiş hesaplama hassasiyeti kaybını kaydetmeyi düşünebilirsiniz. Örneğin:
pas const USER_NUM: u128 = 3;
u128 { let dağıtılacak_token = offset + amount; let per_user_share = token_to_distribute / USER_NUM; println!("per_user_share {}", per_user_share); let recorded_offset = token_to_distribute - per_user_share * USER_NUM; kayıtlı_ofset }
#( fn record_offset_test)[test] { let mut offset: u128 = 0; for i in 1..7 { println!("Round {}", i); offset = distribute(to_yocto)"10"(, offset(; println!)"Offset {}\n", offset); } }
Bu yöntem, hassasiyet kaybı nedeniyle dağıtılmamış jetonları biriktirip yeniden dağıtabilir.
( 3.4 Rust Crate rust-decimal Kütüphanesini Kullanma
Bu kütüphane, etkili hassasiyet hesaplamaları ve yuvarlama hatası olmayan ondalık finansal hesaplamalar için uygundur.
) 3.5 Yuvarlama mekanizmasını dikkate alarak
Akıllı sözleşmeler tasarlanırken, yuvarlama sorunları genellikle "Ben avantaj sağlamak istiyorum, başkaları benim avantajımdan yararlanamaz" ilkesine göre hareket eder. Bu ilkeye göre, eğer aşağı yuvarlama sözleşmeye faydalıysa, aşağı; eğer yukarı yuvarlama sözleşmeye faydalıysa, yukarı; beşli yuvarlama, kimin lehine olduğunu belirleyemediği için nadiren kullanılır.
Bu yöntemleri kullanarak, Rust akıllı sözleşmelerinde daha hassas sayısal hesaplamalar gerçekleştirilebilir, sözleşmenin güvenilirliğini ve adilliğini artırabilir.
![]###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###