Nhật ký phát triển hợp đồng thông minh Rust (7) Vấn đề độ chính xác trong phép toán số thực và số nguyên
Bài viết này sẽ thảo luận về vấn đề độ chính xác của phép toán số thực và số nguyên trong hợp đồng thông minh Rust, cũng như cách viết hợp đồng thông minh tính toán số.
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 tính toán số thực có vấn đề về độ chính xác không thể tránh khỏi. Khi viết hợp đồng thông minh, không nên sử dụng tính toán số thực, đặc biệt là khi xử lý tỷ lệ hoặc lãi suất liên quan đến quyết định kinh tế/tài chính quan trọng.
Trong ngôn ngữ Rust, số thực sử dụng tiêu chuẩn IEEE 754, được biểu diễn bằng hệ thống số khoa học với cơ số 2. Một số số thập phân như ( chẳng hạn như 0.7) không thể được biểu diễn chính xác bằng số thực có độ dài hữu hạn, sẽ có hiện tượng "làm tròn".
Ví dụ, khi phân phối 0,7 NEAR token cho 10 người dùng trên chuỗi công khai NEAR:
gỉ
#[test]
fn precision_test_float() {
let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
assert_eq!(result_0, 0.07, "");
}
Kết quả thực thi cho thấy, giá trị của amount không phải là 0.7 chính xác, mà là giá trị gần đúng 0.69999999999999995559. Kết quả phép chia cũng không chính xác, là 0.06999999999999999 thay vì 0.07 như mong đợi.
Để giải quyết vấn đề này, có thể xem xét việc sử dụng số cố định. Trong NEAR Protocol, thường sử dụng cách biểu diễn 1 NEAR = 10^24 yoctoNEAR:
gỉ
#[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, "");
}
Với cách này, có thể thu được kết quả tính toán giá trị: 0.7 NEAR / 10 = 0.07 NEAR.
2. Vấn đề độ chính xác trong tính toán số nguyên Rust
2.1 Thứ tự toán học
Thứ tự trước sau của phép nhân và phép chia có cùng mức độ ưu tiên có thể ảnh hưởng trực tiếp đến kết quả tính toán:
rỉ sét
#[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,"");
}
Kết quả thực hiện cho thấy result_0 và result_1 không bằng nhau. Nguyên nhân là phép chia số nguyên sẽ loại bỏ độ chính xác thấp hơn mẫu số. Khi tính toán result_1, (a / b) sẽ mất độ chính xác và trở thành 0; trong khi tính toán result_0, việc tính a * c trước có thể tránh mất độ chính xác.
2.2 quy mô quá nhỏ
Số lượng quá nhỏ cũng sẽ dẫn đến vấn đề độ chính xác:
gỉ
#[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");
assert_eq!(result_0, result_1, "");
}
Kết quả cho thấy result_0=12, result_1=13, trong đó kết quả sau gần với giá trị kỳ vọng 13.3333.
3. Cách viết hợp đồng thông minh Rust cho tính toán định lượng
Để nâng cao độ chính xác, có thể áp dụng các biện pháp bảo vệ sau:
3.1 Điều chỉnh thứ tự thực hiện phép toán
Ưu tiên phép nhân số nguyê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 phân số lớn hơn. Ví dụ, biểu diễn 5.123 NEAR dưới dạng 5.123 * 10^10 = 51_230_000_000.
3.3 Mất độ chính xác trong phép toán tích lũy
Ghi lại tổn thất độ chính xác tính toán tích lũy:
rỉ sét
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
}
#(
fn record_offset_test)[test] {
let mut offset: u128 = 0;
cho i trong 1..7 {
offset = distribute(to_yocto)"10"(, offset(;
}
}
Điều này có thể tạm lưu trữ token chưa được phân phát, và phát cho một lần nữa trong lần phân phát tiếp theo.
) 3.4 Sử dụng thư viện Rust Crate rust-decimal
Thư viện này phù hợp với các tính toán tài chính số thập phân cần tính toán chính xác hiệu quả 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, thường áp dụng nguyên tắc "Tôi muốn hưởng lợi, người khác không được lợi dụng tôi". Tùy theo tình huống mà chọn làm tròn xuống hoặc lên, rất ít khi sử dụng phương pháp làm tròn thông thường.
Trang này có thể chứa nội dung của bên thứ ba, được cung cấp chỉ nhằm mục đích thông tin (không phải là tuyên bố/bảo đảm) và không được coi là sự chứng thực cho quan điểm của Gate hoặc là lời khuyên về tài chính hoặc chuyên môn. Xem Tuyên bố từ chối trách nhiệm để biết chi tiết.
12 thích
Phần thưởng
12
8
Chia sẻ
Bình luận
0/400
SandwichHunter
· 07-16 18:07
rust gà mờ lại nằm trên đất rồi
Xem bản gốcTrả lời0
WhaleStalker
· 07-16 17:00
Lỗi mã thật sự nguy hiểm...
Xem bản gốcTrả lời0
GhostInTheChain
· 07-15 16:28
rust vẫn phải điền số thập phân... không đơn giản
Xem bản gốcTrả lời0
BoredWatcher
· 07-13 18:40
Thư viện rust có thể viết hợp đồng?
Xem bản gốcTrả lời0
GasFeeNightmare
· 07-13 18:35
Độ chính xác tính toán phiền phức như phí gas của tôi...
Xem bản gốcTrả lời0
EyeOfTheTokenStorm
· 07-13 18:34
Tỷ lệ mất độ chính xác ảnh hưởng trực tiếp đến tỷ lệ lợi nhuận và thua lỗ, ai còn dùng số dấu phẩy để định lượng? Những con bạc đều đã Rug Pull rồi phải không?
Xem bản gốcTrả lời0
TokenEconomist
· 07-13 18:32
thực ra, mất độ chính xác = f(lệnh_các_phép_toán, hệ_số_tỉ_lệ) ... thư_viện_rust-decimal thật tuyệt
Vấn đề độ chính xác trong phép toán số học của hợp đồng thông minh Rust và các phương án tối ưu hóa
Nhật ký phát triển hợp đồng thông minh Rust (7) Vấn đề độ chính xác trong phép toán số thực và số nguyên
Bài viết này sẽ thảo luận về vấn đề độ chính xác của phép toán số thực và số nguyên trong hợp đồng thông minh Rust, cũng như cách viết hợp đồng thông minh tính toán số.
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 tính toán số thực có vấn đề về độ chính xác không thể tránh khỏi. Khi viết hợp đồng thông minh, không nên sử dụng tính toán số thực, đặc biệt là khi xử lý tỷ lệ hoặc lãi suất liên quan đến quyết định kinh tế/tài chính quan trọng.
Trong ngôn ngữ Rust, số thực sử dụng tiêu chuẩn IEEE 754, được biểu diễn bằng hệ thống số khoa học với cơ số 2. Một số số thập phân như ( chẳng hạn như 0.7) không thể được biểu diễn chính xác bằng số thực có độ dài hữu hạn, sẽ có hiện tượng "làm tròn".
Ví dụ, khi phân phối 0,7 NEAR token cho 10 người dùng trên chuỗi công khai NEAR:
gỉ #[test] fn precision_test_float() { let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
assert_eq!(result_0, 0.07, ""); }
Kết quả thực thi cho thấy, giá trị của amount không phải là 0.7 chính xác, mà là giá trị gần đúng 0.69999999999999995559. Kết quả phép chia cũng không chính xác, là 0.06999999999999999 thay vì 0.07 như mong đợi.
Để giải quyết vấn đề này, có thể xem xét việc sử dụng số cố định. Trong NEAR Protocol, thường sử dụng cách biểu diễn 1 NEAR = 10^24 yoctoNEAR:
gỉ #[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, ""); }
Với cách này, có thể thu được kết quả tính toán giá trị: 0.7 NEAR / 10 = 0.07 NEAR.
2. Vấn đề độ chính xác trong tính toán số nguyên Rust
2.1 Thứ tự toán học
Thứ tự trước sau của phép nhân và phép chia có cùng mức độ ưu tiên có thể ảnh hưởng trực tiếp đến kết quả tính toán:
rỉ sét #[test] fn precision_test_div_before_mul() { let a: u128 = 1_0000; let b: u128 = 10_0000; let c: u128 = 20;
}
Kết quả thực hiện cho thấy result_0 và result_1 không bằng nhau. Nguyên nhân là phép chia số nguyên sẽ loại bỏ độ chính xác thấp hơn mẫu số. Khi tính toán result_1, (a / b) sẽ mất độ chính xác và trở thành 0; trong khi tính toán result_0, việc tính a * c trước có thể tránh mất độ chính xác.
2.2 quy mô quá nhỏ
Số lượng quá nhỏ cũng sẽ dẫn đến vấn đề độ chính xác:
gỉ #[test] fn precision_test_decimals() { let a: u128 = 10; let b: u128 = 3; let c: u128 = 4; let decimal: u128 = 100_0000;
}
Kết quả cho thấy result_0=12, result_1=13, trong đó kết quả sau gần với giá trị kỳ vọng 13.3333.
3. Cách viết hợp đồng thông minh Rust cho tính toán định lượng
Để nâng cao độ chính xác, có thể áp dụng các biện pháp bảo vệ sau:
3.1 Điều chỉnh thứ tự thực hiện phép toán
Ưu tiên phép nhân số nguyê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 phân số lớn hơn. Ví dụ, biểu diễn 5.123 NEAR dưới dạng 5.123 * 10^10 = 51_230_000_000.
3.3 Mất độ chính xác trong phép toán tích lũy
Ghi lại tổn thất độ chính xác tính toán tích lũy:
rỉ sét 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 }
#( fn record_offset_test)[test] { let mut offset: u128 = 0; cho i trong 1..7 { offset = distribute(to_yocto)"10"(, offset(; } }
Điều này có thể tạm lưu trữ token chưa được phân phát, và phát cho một lần nữa trong lần phân phát tiếp theo.
) 3.4 Sử dụng thư viện Rust Crate rust-decimal
Thư viện này phù hợp với các tính toán tài chính số thập phân cần tính toán chính xác hiệu quả 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, thường áp dụng nguyên tắc "Tôi muốn hưởng lợi, người khác không được lợi dụng tôi". Tùy theo tình huống mà chọn làm tròn xuống hoặc lên, rất ít khi sử dụng phương pháp làm tròn thông thường.
![]###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###