Développement de contrats intelligents en Rust : surmonter le problème de la précision des calculs numériques

Problèmes de précision des calculs numériques dans le développement de smart contracts en Rust

1. Problème de précision des opérations sur les nombres à virgule flottante

Le langage Rust prend en charge nativement les opérations sur les nombres à virgule flottante, mais ces opérations présentent des problèmes de précision de calcul inévitables. Lors de l'écriture de smart contracts, il n'est pas recommandé d'utiliser des opérations sur les nombres à virgule flottante, en particulier lors du traitement de ratios ou de taux d'intérêt liés à des décisions économiques/financières importantes.

Le type flottant à double précision f64 dans le langage Rust suit la norme IEEE 754 et utilise la notation scientifique avec une base de 2. Certains décimales ne peuvent pas être représentées avec précision par un nombre flottant de longueur finie, ce qui entraîne un phénomène de "rond".

Par exemple, lors de la distribution de 0,7 NEAR à 10 utilisateurs sur la blockchain NEAR :

rouille #[test] fn precision_test_float() { let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
println!("La valeur du montant : {:.20}", montant); assert_eq!(result_0, 0.07, ""); }

Le résultat affiché montre que la valeur de amount est de 0.69999999999999995559, et non de 0.7 avec précision. Le résultat de la division devient également inexact à 0.06999999999999999, au lieu de 0.07 comme prévu.

Pour résoudre ce problème, on peut envisager d'utiliser des nombres à virgule fixe. Dans le protocole NEAR, on utilise couramment 10^24 yoctoNEAR pour représenter 1 jeton NEAR. Code de test modifié :

rouille #[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, ""); }

Cela permet d'obtenir un résultat de calcul précis : 0,7 NEAR / 10 = 0,07 NEAR.

2. Problème de précision des calculs d'entiers en Rust

L'utilisation des opérations entières peut résoudre le problème de perte de précision des opérations en virgule flottante dans certains scénarios, mais les résultats des calculs entiers ne sont pas non plus entièrement précis et fiables.

2.1 ordre des opérations

Pour les multiplications et les divisions ayant la même priorité arithmétique, le changement de l'ordre peut directement affecter le résultat du calcul, entraînant des problèmes de précision dans les calculs entiers. Par exemple:

rouille #[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,"");

}

Le résultat de l'exécution montre que result_0 et result_1 ne sont pas égaux. La raison en est que la division entière abandonne la précision inférieure au diviseur. Lors du calcul de result_1, (a / b) perd d'abord la précision et devient 0 ; tandis que lors du calcul de result_0, a * c est calculé d'abord pour éviter la perte de précision.

2.2 trop petit en magnitude

Lorsqu'il s'agit de calculs décimaux, les opérations sur les entiers peuvent entraîner une perte de précision :

rouille #[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, "");

}

Les résultats montrent result_0=12, result_1=13, alors que la valeur attendue devrait être 13.3333....

3. Comment écrire des smart contracts Rust pour l'évaluation actuarielle des valeurs numériques

Pour améliorer la précision, les mesures de protection suivantes peuvent être prises :

3.1 Ajustement de l'ordre des opérations

Faire en sorte que la multiplication des entiers ait la priorité sur la division des entiers.

3.2 augmenter l'ordre de grandeur des entiers

Utiliser une plus grande échelle pour créer de plus grandes molécules. Par exemple, représenter 5.123 NEAR peut être exprimé comme 5.123 * 10^10 = 51_230_000_000 pour des calculs ultérieurs.

3.3 perte de précision d'accumulation des calculs

Pour les problèmes de précision inévitables, il est possible d'enregistrer la perte de précision cumulée des calculs. Par exemple :

rouille 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; pour i dans 1..7 { println!("Round {}", i); offset = distribute(to_yocto)"10"(, offset(; println!)"Offset {}\n", offset); } }

Cette méthode permet d'accumuler les tokens restants à chaque distribution et de les distribuer tous ensemble lors de la prochaine distribution, afin d'atteindre finalement l'objectif d'une distribution suffisante.

( 3.4 Utiliser la bibliothèque Rust Crate rust-decimal

Cette bibliothèque est adaptée aux calculs financiers décimaux nécessitant une précision efficace et sans erreur d'arrondi.

) 3.5 Considérer le mécanisme d'arrondi

Dans la conception des smart contracts, le problème d'arrondi est généralement basé sur le principe "Je veux profiter, personne ne doit me tondre". Selon la situation, on choisit d'arrondir vers le bas ou vers le haut, très rarement on utilise l'arrondi classique.

![]###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###

Voir l'original
Cette page peut inclure du contenu de tiers fourni à des fins d'information uniquement. Gate ne garantit ni l'exactitude ni la validité de ces contenus, n’endosse pas les opinions exprimées, et ne fournit aucun conseil financier ou professionnel à travers ces informations. Voir la section Avertissement pour plus de détails.
  • Récompense
  • 8
  • Partager
Commentaire
0/400
rugpull_survivorvip
· 07-14 15:34
Beaucoup de gens sont tombés dans le piège des arrondis.
Voir l'originalRépondre0
WhaleWatchervip
· 07-14 07:46
Le contrôle de la précision est crucial.
Voir l'originalRépondre0
consensus_failurevip
· 07-13 12:52
Utiliser une précision fixe est plus sûr
Voir l'originalRépondre0
Ser_Liquidatedvip
· 07-12 19:41
S’il y a un autre problème de précision, la position sera explosée
Voir l'originalRépondre0
DecentralizeMevip
· 07-11 16:13
Remplacez par des nombres à virgule fixe.
Voir l'originalRépondre0
BoredWatchervip
· 07-11 15:58
Le code est un peu compliqué.
Voir l'originalRépondre0
BrokenDAOvip
· 07-11 15:53
Puissance de calcul totale en surcharge, c'est problématique.
Voir l'originalRépondre0
MEVSandwichMakervip
· 07-11 15:51
C'est un vieux piège, je conseille de l'éviter.
Voir l'originalRépondre0
  • Épingler
Trader les cryptos partout et à tout moment
qrCode
Scan pour télécharger Gate app
Communauté
Français (Afrique)
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)