Masalah Presisi Perhitungan Numerik dalam Pengembangan Smart Contract Rust
1. Masalah presisi dalam perhitungan angka desimal
Bahasa Rust secara native mendukung operasi angka pecahan, tetapi operasi angka pecahan memiliki masalah presisi perhitungan yang tidak dapat dihindari. Saat menulis smart contract, tidak disarankan untuk menggunakan operasi angka pecahan, terutama saat menangani rasio atau suku bunga yang penting untuk keputusan ekonomi/keuangan.
Tipe floating point ganda f64 dalam bahasa Rust mengikuti standar IEEE 754, menggunakan notasi ilmiah berbasis 2 untuk ekspresi. Beberapa desimal tidak dapat diwakili dengan akurat oleh angka floating point dengan panjang terbatas, sehingga terdapat fenomena "pembulatan".
Misalnya, saat mendistribusikan 0,7 NEAR token kepada 10 pengguna di blockchain NEAR:
karat
#[test]
fn precision_test_float() {
let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
println!("Nilai dari jumlah: {:.20}", jumlah);
assert_eq!(result_0, 0.07, "");
}
Hasil output menunjukkan bahwa nilai amount adalah 0.69999999999999995559, bukan 0.7 yang akurat. Hasil operasi pembagian juga menjadi tidak tepat 0.06999999999999999, bukan 0.07 yang diharapkan.
Untuk menyelesaikan masalah ini, dapat mempertimbangkan penggunaan bilangan tetap. Di NEAR Protocol, sering digunakan 10^24 yoctoNEAR untuk mewakili 1 token NEAR. Kode uji yang telah dimodifikasi:
karat
#[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, "");
}
Dengan cara ini, Anda dapat memperoleh hasil perhitungan yang akurat: 0.7 NEAR / 10 = 0.07 NEAR.
2. Masalah presisi perhitungan bilangan bulat Rust
Penggunaan operasi bilangan bulat dapat menyelesaikan masalah kehilangan presisi pada operasi bilangan floating-point dalam beberapa skenario, tetapi hasil perhitungan bilangan bulat juga tidak sepenuhnya akurat dan dapat diandalkan.
2.1 Urutan Operasi
Urutan perubahan antara perkalian dan pembagian dengan prioritas aritmatika yang sama dapat secara langsung mempengaruhi hasil perhitungan, menyebabkan masalah presisi dalam perhitungan bilangan bulat. Misalnya:
karat
#[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,"");
}
Hasil eksekusi menunjukkan bahwa result_0 dan result_1 tidak sama. Alasannya adalah bahwa pembagian bulat akan mengabaikan presisi yang lebih kecil dari pembagi. Saat menghitung result_1, (a / b) terlebih dahulu kehilangan presisi menjadi 0; sementara saat menghitung result_0, terlebih dahulu menghitung a * c menghindari kehilangan presisi.
2.2 ukuran jumlah yang terlalu kecil
Saat melakukan perhitungan desimal, operasi bilangan bulat dapat menyebabkan kehilangan presisi:
karat
#[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, "");
}
Hasil menunjukkan result_0=12, result_1=13, sementara nilai ekspektasi yang sebenarnya seharusnya adalah 13.3333....
3. Bagaimana Menulis Kontrak Pintar Rust untuk Aktuaria Numerik
Untuk meningkatkan akurasi, langkah perlindungan berikut dapat diambil:
3.1 Menyesuaikan urutan operasi perhitungan
Biarkan perkalian bilangan bulat didahulukan daripada pembagian bilangan bulat.
3.2 Menambah besaran bilangan bulat
Gunakan skala yang lebih besar untuk menciptakan molekul yang lebih besar. Misalnya, untuk menyatakan 5.123 NEAR, gunakan 5.123 * 10^10 = 51_230_000_000 untuk perhitungan selanjutnya.
3.3 kehilangan presisi akumulasi operasi
Untuk masalah presisi yang tidak dapat dihindari, dapat mencatat akumulasi kehilangan presisi operasi. Contohnya:
karat
const USER_NUM: u128 = 3;
u128 {
let token_to_distribute = 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;
recorded_offset
}
#(
fn record_offset_test)[test] {
let mut offset: u128 = 0;
untuk i dalam 1..7 {
println!("Round {}", i);
offset = distribute(to_yocto)"10"(, offset(;
println!)"Offset {}\n", offset);
}
}
Metode ini dapat mengakumulasi token sisa dari setiap distribusi, dan mendistribusikannya bersama pada distribusi berikutnya, akhirnya mencapai tujuan distribusi yang cukup.
( 3.4 Menggunakan pustaka Rust Crate rust-decimal
Perpustakaan ini cocok untuk perhitungan keuangan desimal yang membutuhkan perhitungan presisi yang efektif dan tidak memiliki kesalahan pembulatan.
) 3.5 Pertimbangkan mekanisme pembulatan
Dalam desain smart contract, masalah pembulatan biasanya menggunakan prinsip "Saya ingin mengambil keuntungan, orang lain tidak boleh mengambil keuntungan dari saya". Pilih pembulatan ke bawah atau ke atas sesuai dengan situasi, sangat jarang menggunakan pembulatan biasa.
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.
10 Suka
Hadiah
10
8
Bagikan
Komentar
0/400
rugpull_survivor
· 3jam yang lalu
Banyak orang yang terjebak dalam perangkap pembulatan.
Lihat AsliBalas0
WhaleWatcher
· 11jam yang lalu
Kontrol presisi sangat penting
Lihat AsliBalas0
consensus_failure
· 07-13 12:52
Lebih stabil dengan presisi tetap
Lihat AsliBalas0
Ser_Liquidated
· 07-12 19:41
Jika masalah presisi muncul lagi, akan Dilikuidasi.
Lihat AsliBalas0
DecentralizeMe
· 07-11 16:13
Gunakan bilangan tetap sebagai pengganti.
Lihat AsliBalas0
BoredWatcher
· 07-11 15:58
Kodenya agak rumit.
Lihat AsliBalas0
BrokenDAO
· 07-11 15:53
Daya Komputasi total yang berlebih akan menjadi masalah
Lihat AsliBalas0
MEVSandwichMaker
· 07-11 15:51
Ini adalah lubang tua, disarankan untuk dihindari.
Pengembangan smart contract Rust: Mengatasi masalah presisi perhitungan numerik
Masalah Presisi Perhitungan Numerik dalam Pengembangan Smart Contract Rust
1. Masalah presisi dalam perhitungan angka desimal
Bahasa Rust secara native mendukung operasi angka pecahan, tetapi operasi angka pecahan memiliki masalah presisi perhitungan yang tidak dapat dihindari. Saat menulis smart contract, tidak disarankan untuk menggunakan operasi angka pecahan, terutama saat menangani rasio atau suku bunga yang penting untuk keputusan ekonomi/keuangan.
Tipe floating point ganda f64 dalam bahasa Rust mengikuti standar IEEE 754, menggunakan notasi ilmiah berbasis 2 untuk ekspresi. Beberapa desimal tidak dapat diwakili dengan akurat oleh angka floating point dengan panjang terbatas, sehingga terdapat fenomena "pembulatan".
Misalnya, saat mendistribusikan 0,7 NEAR token kepada 10 pengguna di blockchain NEAR:
karat #[test] fn precision_test_float() { let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
println!("Nilai dari jumlah: {:.20}", jumlah); assert_eq!(result_0, 0.07, ""); }
Hasil output menunjukkan bahwa nilai amount adalah 0.69999999999999995559, bukan 0.7 yang akurat. Hasil operasi pembagian juga menjadi tidak tepat 0.06999999999999999, bukan 0.07 yang diharapkan.
Untuk menyelesaikan masalah ini, dapat mempertimbangkan penggunaan bilangan tetap. Di NEAR Protocol, sering digunakan 10^24 yoctoNEAR untuk mewakili 1 token NEAR. Kode uji yang telah dimodifikasi:
karat #[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, ""); }
Dengan cara ini, Anda dapat memperoleh hasil perhitungan yang akurat: 0.7 NEAR / 10 = 0.07 NEAR.
2. Masalah presisi perhitungan bilangan bulat Rust
Penggunaan operasi bilangan bulat dapat menyelesaikan masalah kehilangan presisi pada operasi bilangan floating-point dalam beberapa skenario, tetapi hasil perhitungan bilangan bulat juga tidak sepenuhnya akurat dan dapat diandalkan.
2.1 Urutan Operasi
Urutan perubahan antara perkalian dan pembagian dengan prioritas aritmatika yang sama dapat secara langsung mempengaruhi hasil perhitungan, menyebabkan masalah presisi dalam perhitungan bilangan bulat. Misalnya:
karat #[test] fn precision_test_div_before_mul() { let a: u128 = 1_0000; let b: u128 = 10_0000; let c: u128 = 20;
}
Hasil eksekusi menunjukkan bahwa result_0 dan result_1 tidak sama. Alasannya adalah bahwa pembagian bulat akan mengabaikan presisi yang lebih kecil dari pembagi. Saat menghitung result_1, (a / b) terlebih dahulu kehilangan presisi menjadi 0; sementara saat menghitung result_0, terlebih dahulu menghitung a * c menghindari kehilangan presisi.
2.2 ukuran jumlah yang terlalu kecil
Saat melakukan perhitungan desimal, operasi bilangan bulat dapat menyebabkan kehilangan presisi:
karat #[test] fn precision_test_decimals() { let a: u128 = 10; let b: u128 = 3; let c: u128 = 4; let decimal: u128 = 100_0000;
}
Hasil menunjukkan result_0=12, result_1=13, sementara nilai ekspektasi yang sebenarnya seharusnya adalah 13.3333....
3. Bagaimana Menulis Kontrak Pintar Rust untuk Aktuaria Numerik
Untuk meningkatkan akurasi, langkah perlindungan berikut dapat diambil:
3.1 Menyesuaikan urutan operasi perhitungan
Biarkan perkalian bilangan bulat didahulukan daripada pembagian bilangan bulat.
3.2 Menambah besaran bilangan bulat
Gunakan skala yang lebih besar untuk menciptakan molekul yang lebih besar. Misalnya, untuk menyatakan 5.123 NEAR, gunakan 5.123 * 10^10 = 51_230_000_000 untuk perhitungan selanjutnya.
3.3 kehilangan presisi akumulasi operasi
Untuk masalah presisi yang tidak dapat dihindari, dapat mencatat akumulasi kehilangan presisi operasi. Contohnya:
karat const USER_NUM: u128 = 3;
u128 { let token_to_distribute = 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; recorded_offset }
#( fn record_offset_test)[test] { let mut offset: u128 = 0; untuk i dalam 1..7 { println!("Round {}", i); offset = distribute(to_yocto)"10"(, offset(; println!)"Offset {}\n", offset); } }
Metode ini dapat mengakumulasi token sisa dari setiap distribusi, dan mendistribusikannya bersama pada distribusi berikutnya, akhirnya mencapai tujuan distribusi yang cukup.
( 3.4 Menggunakan pustaka Rust Crate rust-decimal
Perpustakaan ini cocok untuk perhitungan keuangan desimal yang membutuhkan perhitungan presisi yang efektif dan tidak memiliki kesalahan pembulatan.
) 3.5 Pertimbangkan mekanisme pembulatan
Dalam desain smart contract, masalah pembulatan biasanya menggunakan prinsip "Saya ingin mengambil keuntungan, orang lain tidak boleh mengambil keuntungan dari saya". Pilih pembulatan ke bawah atau ke atas sesuai dengan situasi, sangat jarang menggunakan pembulatan biasa.
![]###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###