Problèmes de précision des calculs numériques et solutions d'optimisation dans les smart contracts Rust

robot
Création du résumé en cours

Journal de développement des smart contracts en Rust (7) Problèmes de précision dans les opérations sur les nombres à virgule flottante et les entiers

Cet article discutera des problèmes de précision des opérations sur les nombres à virgule flottante et les entiers dans les smart contracts Rust, ainsi que de la façon d'écrire des smart contracts pour les calculs numériques.

1. Problèmes de précision des calculs en 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 est déconseillé 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.

Dans le langage Rust, les nombres à virgule flottante adoptent la norme IEEE 754 et sont représentés en notation scientifique avec une base de 2. Certains décimaux (, comme 0.7), ne peuvent pas être représentés avec précision par des nombres à virgule flottante de longueur finie, ce qui entraîne un phénomène d'"arrondi".

Par exemple, lors de la distribution de 0,7 token 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;
assert_eq!(result_0, 0.07, ""); }

Le résultat de l'exécution montre que la valeur de amount n'est pas exactement 0.7, mais plutôt une valeur approximative de 0.69999999999999995559. Le résultat de la division n'est pas non plus précis, étant 0.06999999999999999 au lieu de l'attendu 0.07.

Pour résoudre ce problème, il est possible d'envisager l'utilisation de nombres à virgule fixe. Dans le protocole NEAR, on utilise généralement la notation 1 NEAR = 10^24 yoctoNEAR :

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

Ainsi, on obtient le résultat du calcul actuariel: 0,7 NEAR / 10 = 0,07 NEAR.

2. Problème de précision du calcul des entiers en Rust

2.1 ordre des opérations

L'ordre de multiplication et de division ayant la même priorité arithmétique peut directement influencer le résultat du calcul:

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 sa précision et devient 0 ; tandis que lors du calcul de result_0, le calcul de a * c permet d'éviter la perte de précision.

2.2 trop petit ordre de grandeur

Une taille trop petite peut également entraîner des problèmes 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");

assert_eq!(result_0, result_1, "");

}

Les résultats montrent result_0=12, result_1=13, ce dernier étant plus proche de la valeur attendue de 13.3333.

3. Comment écrire des smart contracts Rust pour l'évaluation numérique

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

3.1 Ajuster 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 des ordres de grandeur plus élevés pour créer des molécules plus grandes. Par exemple, représenter 5,123 NEAR comme 5,123 * 10^10 = 51_230_000_000.

3.3 perte de précision dans l'accumulation des calculs

Enregistrer la perte de précision des calculs cumulés :

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

Cela permet de conserver les tokens non distribués en attente, et de les distribuer lors de la prochaine distribution.

) 3.4 Utilisation de la bibliothèque Rust Crate rust-decimal

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

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

Lors de la conception de smart contracts, on adopte généralement le principe "Je veux en profiter, les autres ne doivent pas me tondre". Selon les circonstances, on choisit d'arrondir à l'entier inférieur ou supérieur, et on utilise très rarement l'arrondi traditionnel.

![]###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
SandwichHuntervip
· 07-16 18:07
le petit poulet rust est encore allongé par terre
Voir l'originalRépondre0
WhaleStalkervip
· 07-16 17:00
Les bugs de code sont vraiment mortels...
Voir l'originalRépondre0
GhostInTheChainvip
· 07-15 16:28
rust doit encore remplir des décimales... pas simple
Voir l'originalRépondre0
BoredWatchervip
· 07-13 18:40
Une bibliothèque Rust peut-elle écrire des contrats ?
Voir l'originalRépondre0
GasFeeNightmarevip
· 07-13 18:35
La précision des calculs est aussi ennuyeuse que mes frais de gas...
Voir l'originalRépondre0
EyeOfTheTokenStormvip
· 07-13 18:34
Le taux de perte de précision affecte directement le ratio de profit et de perte, qui utilise encore des nombres à virgule flottante pour la quantification ? Les parieurs ont dû faire un Rug Pull, non ?
Voir l'originalRépondre0
TokenEconomistvip
· 07-13 18:32
en fait, perte de précision = f(ordre_des_ops, facteur_d'échelle) ... rust-decimal lib pour la victoire
Voir l'originalRépondre0
just_another_walletvip
· 07-13 18:25
Les nombres à virgule flottante sont vraiment problématiques...
Voir l'originalRépondre0
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)