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###

B-11.6%
View Original
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.
  • Reward
  • 5
  • Share
Comment
0/400
CrossChainBreathervip
· 07-16 01:40
Float operations are a pain in the ass
View OriginalReply0
CantAffordPancakevip
· 07-15 09:39
Don't mention precision, my account has been wiped out.
View OriginalReply0
MaticHoleFillervip
· 07-13 03:03
Stuck all day on precision issues, painfully losing everything.
View OriginalReply0
StablecoinAnxietyvip
· 07-13 02:58
It's a bit overwhelming to have both algorithms and finance~
View OriginalReply0
GhostAddressMinervip
· 07-13 02:34
Another typical case of shifting the responsibility for a precision issue to the standards, secretly not knowing how much capital has been trapped, on-chain data won't lie.
View OriginalReply0
Trade Crypto Anywhere Anytime
qrCode
Scan to download Gate app
Community
English
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)