📢 Gate Square #MBG Posting Challenge# is Live— Post for MBG Rewards!
Want a share of 1,000 MBG? Get involved now—show your insights and real participation to become an MBG promoter!
💰 20 top posts will each win 50 MBG!
How to Participate:
1️⃣ Research the MBG project
Share your in-depth views on MBG’s fundamentals, community governance, development goals, and tokenomics, etc.
2️⃣ Join and share your real experience
Take part in MBG activities (CandyDrop, Launchpool, or spot trading), and post your screenshots, earnings, or step-by-step tutorials. Content can include profits, beginner-friendl
Rust smart contracts numerical calculation: Pitfall avoidance guide and best practices
Numerical Precision in Rust Smart Contracts
In smart contracts programming, the precision of numerical calculations is particularly important. This article will explore common numerical precision issues and solutions in Rust smart contracts.
1. The Precision Problem of Floating-Point Arithmetic
The Rust language natively supports floating-point arithmetic, but there are unavoidable precision issues with floating-point calculations. It is not recommended to use floating-point arithmetic when dealing with ratios or interest rates that involve important economic/financial decisions.
The double-precision floating-point type f64 in Rust follows the IEEE 754 standard and uses scientific notation with a base of 2. Certain decimals like ( and 0.7) cannot be accurately represented with a finite-length floating-point number, leading to a "rounding" phenomenon.
For example, when distributing 0.7 NEAR tokens to 10 users on the NEAR public chain:
rust let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
The actual value of amount is 0.69999999999999995559, and the result of result_0 is 0.06999999999999999, instead of the expected 0.07.
To solve this problem, you can consider using fixed-point representation. In the NEAR Protocol, 10^24 is typically used as the denominator, i.e., 1 NEAR = 10^24 yoctoNEAR. The modified calculation method is as follows:
rust 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;
This allows for an exact calculation result: 0.7 NEAR / 10 = 0.07 NEAR.
2. The Issue of Integer Calculation Precision in Rust
Using integer calculations can solve floating point precision issues in certain scenarios, but there are still some factors that affect calculation accuracy.
2.1 Order of Operations
The order of multiplication and division with the same arithmetic priority can directly affect the calculation results. For example:
rust 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");
The calculation results of result_0 and result_1 are different because integer division truncates precision that is less than the divisor.
2.2 too small magnitude
When it comes to calculations of smaller magnitudes, precision issues may also arise:
rust 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");
The calculation results of result_0 and result_1 are different, and result_1 is closer to the actual expected value.
3. How to Write Numerically Accurate Rust Smart Contracts
To improve the numerical calculation accuracy in Rust smart contracts, the following measures can be taken:
3.1 Adjust the order of operations
Let integer multiplication take precedence over integer division.
Increase the order of magnitude of integers 3.2
Use a larger magnitude to create larger molecules and improve computational accuracy.
3.3 The loss of accumulated computational precision
For unavoidable integer calculation precision issues, you may consider recording the cumulative loss of calculation precision. For example:
rust 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 }
This method can gradually compensate for accuracy loss in multiple rounds of distribution.
( 3.4 Using Rust Crate library rust-decimal
This library is suitable for decimal financial calculations that require precise computation and have no rounding errors.
) 3.5 Consider the rounding mechanism
When designing smart contracts, rounding issues usually follow the principle of "I want to take advantage, and others should not exploit me." Choose to round down, round up, or round to the nearest according to the situation.
By adopting these methods, the precision and reliability of numerical calculations in Rust smart contracts can be significantly improved.
![]###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###