Artikel ini akan membahas masalah akurasi numerik dalam pemrograman smart contract Rust, terutama termasuk masalah akurasi operasi floating-point, masalah akurasi perhitungan integer, serta cara menulis smart contract Rust untuk akurasi numerik dan hal-hal terkait.
1. Masalah Presisi dalam Operasi Bilangan Floating Point
Bahasa Rust secara alami mendukung operasi bilangan floating point, tetapi operasi bilangan floating point memiliki masalah presisi perhitungan yang tidak dapat dihindari. Saat menulis smart contract, tidak disarankan untuk menggunakan operasi bilangan floating point, terutama saat menangani rasio atau suku bunga yang melibatkan keputusan ekonomi/keuangan yang penting.
Tipe titik mengambang ganda f64 dalam bahasa Rust mengikuti standar IEEE 754, menggunakan notasi ilmiah dengan basis 2 untuk menyatakan. Namun, beberapa desimal ( seperti 0.7) tidak dapat diwakili secara akurat dengan bilangan titik mengambang berukuran terbatas, sehingga akan ada fenomena "pembulatan".
Sebagai contoh, mendistribusikan 0,7 NEAR token kepada sepuluh pengguna di blockchain NEAR, hasil perhitungan aktual dapat menunjukkan ketidakakuratan:
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}", amount);
assert_eq!(result_0, 0.07, "");
}
Hasil eksekusi menunjukkan bahwa nilai amount tidak akurat 0.7, melainkan nilai yang sangat mendekati 0.69999999999999995559. Hasil pembagian lebih lanjut juga menjadi tidak tepat 0.06999999999999999, bukan 0.07 yang diharapkan.
Untuk menyelesaikan masalah ini, dapat dipertimbangkan untuk menggunakan angka tetap. Di NEAR Protocol, biasanya menggunakan 10^24 sebagai penyebut, yaitu 10^24 yoctoNEAR setara dengan 1 token NEAR.
Kode uji yang telah dimodifikasi adalah sebagai berikut:
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 integer Rust
Meskipun penggunaan perhitungan bilangan bulat dapat mengatasi masalah kehilangan presisi angka desimal dalam beberapa skenario, namun hasil dari perhitungan bilangan bulat juga tidak sepenuhnya akurat dan dapat diandalkan. Beberapa alasan yang mempengaruhi presisi perhitungan bilangan bulat termasuk:
2.1 Urutan Operasi
Urutan perubahan antara perkalian dan pembagian dengan prioritas aritmetika yang sama dapat langsung mempengaruhi hasil perhitungan. Contohnya:
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 pengujian menunjukkan bahwa hasil perhitungan result_0 dan result_1 berbeda. Ini disebabkan karena untuk pembagian bilangan bulat, presisi yang lebih kecil dari penyebut akan diabaikan. Dalam perhitungan result_1, (a / b) akan kehilangan presisi perhitungan terlebih dahulu, menjadi 0; sedangkan dalam perhitungan result_0, menghitung a * c terlebih dahulu dapat menghindari kehilangan presisi.
2.2 tingkat jumlah yang terlalu kecil
Ketika melibatkan perhitungan dalam skala yang lebih kecil, masalah presisi juga dapat muncul:
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 tes menunjukkan bahwa hasil perhitungan result_0 dan result_1 berbeda, dan result_1 = 13 lebih dekat dengan nilai perhitungan yang diharapkan secara nyata yaitu 13.3333....
3. Cara Menulis Kontrak Pintar Rust untuk Actuari Numerik
Untuk meningkatkan akurasi, langkah-langkah perlindungan berikut dapat diambil:
3.1 Menyesuaikan urutan operasi perhitungan
Mengutamakan perkalian integer daripada pembagian integer.
3.2 Meningkatkan derajat bilangan bulat
Gunakan skala yang lebih besar untuk menciptakan molekul yang lebih besar. Misalnya, 5.123 NEAR dapat dinyatakan sebagai 5.123 * 10^10 = 51_230_000_000.
3.3 Kehilangan presisi akumulasi operasi
Untuk masalah presisi perhitungan integer yang tidak dapat dihindari, Anda dapat mempertimbangkan untuk mencatat akumulasi kehilangan presisi perhitungan. Misalnya:
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 dan mendistribusikan kembali token yang tidak didistribusikan karena kehilangan presisi.
( 3.4 Menggunakan pustaka Rust Crate rust-decimal
Perpustakaan ini cocok untuk perhitungan keuangan desimal yang memerlukan perhitungan presisi yang efektif dan tidak memiliki kesalahan pembulatan.
) 3.5 Pertimbangkan mekanisme pembulatan
Dalam merancang smart contract, masalah pembulatan biasanya mengikuti prinsip "saya ingin mendapatkan keuntungan, orang lain tidak boleh mengambil keuntungan dari saya". Berdasarkan prinsip ini, jika pembulatan ke bawah menguntungkan kontrak, maka dibulatkan ke bawah; jika pembulatan ke atas menguntungkan kontrak, maka dibulatkan ke atas; pembulatan biasa jarang digunakan karena tidak dapat dipastikan siapa yang diuntungkan.
Dengan menggunakan metode ini, perhitungan numerik yang lebih akurat dapat dicapai dalam kontrak pintar Rust, meningkatkan keandalan dan keadilan kontrak.
Halaman ini mungkin berisi konten pihak ketiga, yang disediakan untuk tujuan informasi saja (bukan pernyataan/jaminan) dan tidak boleh dianggap sebagai dukungan terhadap pandangannya oleh Gate, atau sebagai nasihat keuangan atau profesional. Lihat Penafian untuk detailnya.
14 Suka
Hadiah
14
8
Bagikan
Komentar
0/400
ZeroRushCaptain
· 07-17 09:17
Ah, mulai lagi mengutak-atik barang ini. Terakhir kali masalah presisi membuat saya terkena kontrak posisi lock-up dan merugi sampai makan tanah.
Lihat AsliBalas0
BlockchainFries
· 07-16 10:52
Fungsi float ini terlalu merepotkan, siapa yang menggunakannya akan sial.
Lihat AsliBalas0
MoonBoi42
· 07-15 08:48
Presisi floating-point mana yang lebih nyaman daripada fixed-point~
Lihat AsliBalas0
OnchainDetective
· 07-14 09:54
Jelas bahwa kesalahan presisi adalah celah yang dimanfaatkan oleh beberapa cross-chain bridges oleh Hacker, sangat mengerikan.
Lihat AsliBalas0
HashRateHermit
· 07-14 09:54
Masalah akurasi benar-benar membuatku terjebak.
Lihat AsliBalas0
GateUser-e87b21ee
· 07-14 09:47
Sekali lagi berurusan dengan masalah titik desimal, kepala saya pusing.
Lihat AsliBalas0
BearMarketSurvivor
· 07-14 09:45
Keterampilan ini adalah sesuatu yang tidak bisa dihindari oleh siapa pun, orang ini mengerti.
Lihat AsliBalas0
AirdropLicker
· 07-14 09:35
Pembulatan angka desimal ini terlalu dalam, sudah terperangkap sebelumnya.
Teknik dan perhatian dalam perhitungan nilai dalam smart contract Rust
Rust smart contract养成日记(7):数值精算
Artikel ini akan membahas masalah akurasi numerik dalam pemrograman smart contract Rust, terutama termasuk masalah akurasi operasi floating-point, masalah akurasi perhitungan integer, serta cara menulis smart contract Rust untuk akurasi numerik dan hal-hal terkait.
1. Masalah Presisi dalam Operasi Bilangan Floating Point
Bahasa Rust secara alami mendukung operasi bilangan floating point, tetapi operasi bilangan floating point memiliki masalah presisi perhitungan yang tidak dapat dihindari. Saat menulis smart contract, tidak disarankan untuk menggunakan operasi bilangan floating point, terutama saat menangani rasio atau suku bunga yang melibatkan keputusan ekonomi/keuangan yang penting.
Tipe titik mengambang ganda f64 dalam bahasa Rust mengikuti standar IEEE 754, menggunakan notasi ilmiah dengan basis 2 untuk menyatakan. Namun, beberapa desimal ( seperti 0.7) tidak dapat diwakili secara akurat dengan bilangan titik mengambang berukuran terbatas, sehingga akan ada fenomena "pembulatan".
Sebagai contoh, mendistribusikan 0,7 NEAR token kepada sepuluh pengguna di blockchain NEAR, hasil perhitungan aktual dapat menunjukkan ketidakakuratan:
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}", amount); assert_eq!(result_0, 0.07, ""); }
Hasil eksekusi menunjukkan bahwa nilai amount tidak akurat 0.7, melainkan nilai yang sangat mendekati 0.69999999999999995559. Hasil pembagian lebih lanjut juga menjadi tidak tepat 0.06999999999999999, bukan 0.07 yang diharapkan.
Untuk menyelesaikan masalah ini, dapat dipertimbangkan untuk menggunakan angka tetap. Di NEAR Protocol, biasanya menggunakan 10^24 sebagai penyebut, yaitu 10^24 yoctoNEAR setara dengan 1 token NEAR.
Kode uji yang telah dimodifikasi adalah sebagai berikut:
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 integer Rust
Meskipun penggunaan perhitungan bilangan bulat dapat mengatasi masalah kehilangan presisi angka desimal dalam beberapa skenario, namun hasil dari perhitungan bilangan bulat juga tidak sepenuhnya akurat dan dapat diandalkan. Beberapa alasan yang mempengaruhi presisi perhitungan bilangan bulat termasuk:
2.1 Urutan Operasi
Urutan perubahan antara perkalian dan pembagian dengan prioritas aritmetika yang sama dapat langsung mempengaruhi hasil perhitungan. Contohnya:
karat #[test] fn precision_test_div_before_mul() { let a: u128 = 1_0000; let b: u128 = 10_0000; let c: u128 = 20;
}
Hasil pengujian menunjukkan bahwa hasil perhitungan result_0 dan result_1 berbeda. Ini disebabkan karena untuk pembagian bilangan bulat, presisi yang lebih kecil dari penyebut akan diabaikan. Dalam perhitungan result_1, (a / b) akan kehilangan presisi perhitungan terlebih dahulu, menjadi 0; sedangkan dalam perhitungan result_0, menghitung a * c terlebih dahulu dapat menghindari kehilangan presisi.
2.2 tingkat jumlah yang terlalu kecil
Ketika melibatkan perhitungan dalam skala yang lebih kecil, masalah presisi juga dapat muncul:
karat #[test] fn precision_test_decimals() { let a: u128 = 10; let b: u128 = 3; let c: u128 = 4; let decimal: u128 = 100_0000;
}
Hasil tes menunjukkan bahwa hasil perhitungan result_0 dan result_1 berbeda, dan result_1 = 13 lebih dekat dengan nilai perhitungan yang diharapkan secara nyata yaitu 13.3333....
3. Cara Menulis Kontrak Pintar Rust untuk Actuari Numerik
Untuk meningkatkan akurasi, langkah-langkah perlindungan berikut dapat diambil:
3.1 Menyesuaikan urutan operasi perhitungan
Mengutamakan perkalian integer daripada pembagian integer.
3.2 Meningkatkan derajat bilangan bulat
Gunakan skala yang lebih besar untuk menciptakan molekul yang lebih besar. Misalnya, 5.123 NEAR dapat dinyatakan sebagai 5.123 * 10^10 = 51_230_000_000.
3.3 Kehilangan presisi akumulasi operasi
Untuk masalah presisi perhitungan integer yang tidak dapat dihindari, Anda dapat mempertimbangkan untuk mencatat akumulasi kehilangan presisi perhitungan. Misalnya:
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 dan mendistribusikan kembali token yang tidak didistribusikan karena kehilangan presisi.
( 3.4 Menggunakan pustaka Rust Crate rust-decimal
Perpustakaan ini cocok untuk perhitungan keuangan desimal yang memerlukan perhitungan presisi yang efektif dan tidak memiliki kesalahan pembulatan.
) 3.5 Pertimbangkan mekanisme pembulatan
Dalam merancang smart contract, masalah pembulatan biasanya mengikuti prinsip "saya ingin mendapatkan keuntungan, orang lain tidak boleh mengambil keuntungan dari saya". Berdasarkan prinsip ini, jika pembulatan ke bawah menguntungkan kontrak, maka dibulatkan ke bawah; jika pembulatan ke atas menguntungkan kontrak, maka dibulatkan ke atas; pembulatan biasa jarang digunakan karena tidak dapat dipastikan siapa yang diuntungkan.
Dengan menggunakan metode ini, perhitungan numerik yang lebih akurat dapat dicapai dalam kontrak pintar Rust, meningkatkan keandalan dan keadilan kontrak.
![]###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###