Trong lập trình hợp đồng thông minh, vấn đề độ chính xác của phép toán số là rất quan trọng. Bài viết này sẽ khám phá các vấn đề tính toán số phổ biến trong hợp đồng thông minh Rust và các giải pháp.
1. Vấn đề độ chính xác trong phép toán số thực
Ngôn ngữ Rust hỗ trợ tính toán số thực một cách tự nhiên, nhưng có vấn đề về độ chính xác không thể tránh khỏi trong tính toán số thực. Khi xử lý các tỷ lệ hoặc lãi suất liên quan đến quyết định kinh tế/tài chính quan trọng, không nên sử dụng tính toán số thực.
Loại số thực độ chính xác gấp đôi f64 trong ngôn ngữ Rust tuân theo tiêu chuẩn IEEE 754, sử dụng ký hiệu khoa học với cơ số 2. Một số số thập phân ( như 0.7) không thể được biểu diễn chính xác bằng số thực có số lượng bit hữu hạn, sẽ tồn tại hiện tượng "làm tròn".
Ví dụ, khi phân phát 0.7 token NEAR cho 10 người dùng trên chuỗi công khai NEAR:
gỉ
let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
giá trị thực tế của amount là 0.69999999999999995559, kết quả của result_0 là 0.06999999999999999, không phải là 0.07 như đã dự kiến.
Để giải quyết vấn đề này, có thể xem xét việc sử dụng phương pháp biểu diễn số cố định. Trong NEAR Protocol, thường sử dụng 10^24 làm mẫu số, tức là 1 NEAR = 10^24 yoctoNEAR. Cách tính toán đã được sửa đổi như sau:
gỉ
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;
Cách này có thể đạt được kết quả tính toán chính xác: 0.7 NEAR / 10 = 0.07 NEAR.
2. Vấn đề độ chính xác trong tính toán số nguyên Rust
Việc sử dụng số nguyên để tính toán mặc dù có thể giải quyết một số vấn đề về độ chính xác của số thực trong một số tình huống, nhưng vẫn tồn tại một số yếu tố ảnh hưởng đến độ chính xác của phép tính.
2.1 Thứ tự thực hiện
Thứ tự trước và sau của phép nhân và phép chia cùng cấp độ ưu tiên toán học có thể ảnh hưởng trực tiếp đến kết quả tính toán. Ví dụ:
rỉ sét
let a: u128 = 1_0000;
let b: u128 = 10_0000;
let c: u128 = 20;
// result_0 = a * c / b
let result_0 = a.checked_mul(c).expect("ERR_MUL").checked_div(b).expect("ERR_DIV");
// result_1 = a / b * c
let result_1 = a.checked_div(b).expect("ERR_DIV").checked_mul(c).expect("ERR_MUL");
Kết quả của result_0 và result_1 khác nhau, nguyên nhân là do phép chia số nguyên sẽ bỏ qua độ chính xác nhỏ hơn số chia.
2.2 quy mô quá nhỏ
Khi liên quan đến các phép tính ở quy mô nhỏ hơn, cũng có thể dẫn đến vấn đề về độ chính xác:
gỉ
let a: u128 = 10;
let b: u128 = 3;
let c: u128 = 4;
let decimal: u128 = 100_0000;
// result_0 = (a / b) * c
let result_0 = a.checked_div(b).expect("ERR_DIV").checked_mul(c).expect("ERR_MUL");
// result_1 = (a * decimal / b) * c / decimal;
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");
Kết quả của result_0 và result_1 khác nhau, và result_1 gần với giá trị kỳ vọng thực tế hơn.
3. Làm thế nào để viết hợp đồng thông minh Rust cho tính toán số
Để nâng cao độ chính xác của phép toán số trong hợp đồng thông minh Rust, có thể thực hiện các biện pháp sau:
3.1 Điều chỉnh thứ tự thực hiện phép toán
Hãy để phép nhân số nguyên ưu tiên hơn phép chia số nguyên.
3.2 Tăng cường độ lớn của số nguyên
Sử dụng quy mô lớn hơn để tạo ra các phân tử lớn hơn, cải thiện độ chính xác của phép toán.
3.3 Tích lũy tổn thất độ chính xác tính toán
Đối với vấn đề độ chính xác của phép tính số nguyên không thể tránh khỏi, có thể xem xét việc ghi lại tổn thất độ chính xác tích lũy. Ví dụ:
gỉ
const USER_NUM: u128 = 3;
u128 {
let token_to_distribute = offset + amount;
let per_user_share = token_to_distribute / USER_NUM;
let recorded_offset = token_to_distribute - per_user_share * USER_NUM;
recorded_offset
}
Phương pháp này có thể dần dần bù đắp cho sự mất mát độ chính xác trong nhiều vòng phân phối.
( 3.4 Sử dụng thư viện Rust Crate rust-decimal
Thư viện này phù hợp cho các phép toán tài chính với số thập phân cần tính toán chính xác và không có sai số làm tròn.
) 3.5 Xem xét cơ chế làm tròn
Trong thiết kế hợp đồng thông minh, vấn đề làm tròn thường tuân theo nguyên tắc "Tôi muốn thu lợi, người khác không được lợi dụng tôi". Chọn làm tròn xuống, làm tròn lên hoặc làm tròn thông thường tùy theo tình huống.
Bằng cách áp dụng những phương pháp này, có thể nâng cao đáng kể độ chính xác và độ tin cậy của phép tính số trong hợp đồng thông minh Rust.
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.
7 thích
Phần thưởng
7
3
Chia sẻ
Bình luận
0/400
MaticHoleFiller
· 17giờ trước
Cả ngày mắc kẹt trong vấn đề độ chính xác, đau lòng mất trắng.
Xem bản gốcTrả lời0
StablecoinAnxiety
· 17giờ trước
Lại cần thuật toán lại cần tài chính, hơi đau đầu~
Xem bản gốcTrả lời0
GhostAddressMiner
· 18giờ trước
Một ví dụ điển hình về việc đổ lỗi cho tiêu chuẩn trong vấn đề độ chính xác, không biết đã rút tiền bao nhiêu trong bóng tối, dữ liệu on-chain sẽ không nói dối.
Rust hợp đồng thông minh số liệu tính toán: Hướng dẫn tránh cạm bẫy và thực tiễn tốt nhất
Tính toán số học trong hợp đồng thông minh Rust
Trong lập trình hợp đồng thông minh, vấn đề độ chính xác của phép toán số là rất quan trọng. Bài viết này sẽ khám phá các vấn đề tính toán số phổ biến trong hợp đồng thông minh Rust và các giải pháp.
1. Vấn đề độ chính xác trong phép toán số thực
Ngôn ngữ Rust hỗ trợ tính toán số thực một cách tự nhiên, nhưng có vấn đề về độ chính xác không thể tránh khỏi trong tính toán số thực. Khi xử lý các tỷ lệ hoặc lãi suất liên quan đến quyết định kinh tế/tài chính quan trọng, không nên sử dụng tính toán số thực.
Loại số thực độ chính xác gấp đôi f64 trong ngôn ngữ Rust tuân theo tiêu chuẩn IEEE 754, sử dụng ký hiệu khoa học với cơ số 2. Một số số thập phân ( như 0.7) không thể được biểu diễn chính xác bằng số thực có số lượng bit hữu hạn, sẽ tồn tại hiện tượng "làm tròn".
Ví dụ, khi phân phát 0.7 token NEAR cho 10 người dùng trên chuỗi công khai NEAR:
gỉ let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
giá trị thực tế của amount là 0.69999999999999995559, kết quả của result_0 là 0.06999999999999999, không phải là 0.07 như đã dự kiến.
Để giải quyết vấn đề này, có thể xem xét việc sử dụng phương pháp biểu diễn số cố định. Trong NEAR Protocol, thường sử dụng 10^24 làm mẫu số, tức là 1 NEAR = 10^24 yoctoNEAR. Cách tính toán đã được sửa đổi như sau:
gỉ 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;
Cách này có thể đạt được kết quả tính toán chính xác: 0.7 NEAR / 10 = 0.07 NEAR.
2. Vấn đề độ chính xác trong tính toán số nguyên Rust
Việc sử dụng số nguyên để tính toán mặc dù có thể giải quyết một số vấn đề về độ chính xác của số thực trong một số tình huống, nhưng vẫn tồn tại một số yếu tố ảnh hưởng đến độ chính xác của phép tính.
2.1 Thứ tự thực hiện
Thứ tự trước và sau của phép nhân và phép chia cùng cấp độ ưu tiên toán học có thể ảnh hưởng trực tiếp đến kết quả tính toán. Ví dụ:
rỉ sét let a: u128 = 1_0000; let b: u128 = 10_0000; let c: u128 = 20;
// result_0 = a * c / b let result_0 = a.checked_mul(c).expect("ERR_MUL").checked_div(b).expect("ERR_DIV");
// result_1 = a / b * c let result_1 = a.checked_div(b).expect("ERR_DIV").checked_mul(c).expect("ERR_MUL");
Kết quả của result_0 và result_1 khác nhau, nguyên nhân là do phép chia số nguyên sẽ bỏ qua độ chính xác nhỏ hơn số chia.
2.2 quy mô quá nhỏ
Khi liên quan đến các phép tính ở quy mô nhỏ hơn, cũng có thể dẫn đến vấn đề về độ chính xác:
gỉ let a: u128 = 10; let b: u128 = 3; let c: u128 = 4; let decimal: u128 = 100_0000;
// result_0 = (a / b) * c let result_0 = a.checked_div(b).expect("ERR_DIV").checked_mul(c).expect("ERR_MUL");
// result_1 = (a * decimal / b) * c / decimal; 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");
Kết quả của result_0 và result_1 khác nhau, và result_1 gần với giá trị kỳ vọng thực tế hơn.
3. Làm thế nào để viết hợp đồng thông minh Rust cho tính toán số
Để nâng cao độ chính xác của phép toán số trong hợp đồng thông minh Rust, có thể thực hiện các biện pháp sau:
3.1 Điều chỉnh thứ tự thực hiện phép toán
Hãy để phép nhân số nguyên ưu tiên hơn phép chia số nguyên.
3.2 Tăng cường độ lớn của số nguyên
Sử dụng quy mô lớn hơn để tạo ra các phân tử lớn hơn, cải thiện độ chính xác của phép toán.
3.3 Tích lũy tổn thất độ chính xác tính toán
Đối với vấn đề độ chính xác của phép tính số nguyên không thể tránh khỏi, có thể xem xét việc ghi lại tổn thất độ chính xác tích lũy. Ví dụ:
gỉ const USER_NUM: u128 = 3;
u128 { let token_to_distribute = offset + amount; let per_user_share = token_to_distribute / USER_NUM; let recorded_offset = token_to_distribute - per_user_share * USER_NUM; recorded_offset }
Phương pháp này có thể dần dần bù đắp cho sự mất mát độ chính xác trong nhiều vòng phân phối.
( 3.4 Sử dụng thư viện Rust Crate rust-decimal
Thư viện này phù hợp cho các phép toán tài chính với số thập phân cần tính toán chính xác và không có sai số làm tròn.
) 3.5 Xem xét cơ chế làm tròn
Trong thiết kế hợp đồng thông minh, vấn đề làm tròn thường tuân theo nguyên tắc "Tôi muốn thu lợi, người khác không được lợi dụng tôi". Chọn làm tròn xuống, làm tròn lên hoặc làm tròn thông thường tùy theo tình huống.
Bằng cách áp dụng những phương pháp này, có thể nâng cao đáng kể độ chính xác và độ tin cậy của phép tính số trong hợp đồng thông minh Rust.
![]###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###