Problèmes de précision des calculs entiers dans les smart contracts Rust et solutions

Journal de développement des smart contracts Rust (7) : Problème de précision dans le calcul des entiers

Récapitulatif des éditions précédentes :

  • Journal de développement des smart contracts Rust (1) Définition des données d'état du contrat et mise en œuvre des méthodes
  • Journal de développement des smart contracts Rust ( Écriture de tests unitaires pour les smart contracts Rust
  • Journal de développement des smart contracts Rust )3( Déploiement des smart contracts Rust, appel de fonctions et utilisation de l'Explorer
  • Journal de développement de smart contracts Rust )4( Débordement d'entiers dans les smart contracts Rust
  • Journal de développement de contrats intelligents Rust )5( attaque de réentrance
  • Journal de développement des smart contracts Rust )6( attaque par déni de service

1. Problèmes de précision des calculs à virgule flottante

Contrairement à Solidity, Rust prend en charge nativement les opérations à virgule flottante. Cependant, les opérations à virgule flottante présentent des problèmes de précision inévitables, il n'est donc pas recommandé de les utiliser dans des smart contracts, en particulier lors du traitement de ratios ou de taux d'intérêt liés à des décisions économiques/financières importantes.

Rust suit la norme IEEE 754 pour représenter les nombres à virgule flottante. Le type à virgule flottante double précision f64 est représenté en interne dans l'ordinateur par la notation scientifique binaire.

Certaines décimales peuvent être représentées avec une précision binaire à un nombre fini de chiffres, par exemple 0.8125 peut être représenté par 0.1101. Cependant, une décimale comme 0.7 produira une représentation binaire en boucle infinie, ce qui rend impossible une représentation précise avec un nombre fini de chiffres flottants, entraînant un problème d'"arrondi".

Dans l'exemple de distribution de 0,7 NEAR tokens à 10 utilisateurs sur la blockchain NEAR :

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

Le résultat montre que la valeur réelle de amount est 0.69999999999999995559, result_0 est 0.06999999999999999, ce qui n'est pas égal à l'attendu de 0.07.

Pour résoudre ce problème, on peut utiliser des nombres à virgule fixe. Dans NEAR, 1 jeton NEAR est généralement représenté par 10^24 yoctoNEAR. Code modifié :

rouille
#) fn precision_test_integer[test]( { 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 précis : 0,7 NEAR / 10 = 0,07 NEAR.

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

Bien que les opérations sur les entiers puissent résoudre des problèmes de précision des nombres à virgule flottante dans certains cas, les calculs sur les entiers présentent également des problèmes de précision.

) 2.1 ordre des opérations

Les opérations de multiplication et de division du même niveau, le changement d'ordre peut influencer le résultat :

rouille

fn precision_test_div_before_mul[test]( { let a: u128 = 1_0000; let b: u128 = 10_0000; let c: u128 = 20;

let result_0 = a.checked_mul)c(.unwrap)(.checked_div)b(.unwrap)(;
let result_1 = a.checked_div)b(.unwrap)(.checked_mul)c(.unwrap)(;

assert_eq!)result_0,result_1(;

}

Les résultats montrent result_0 = 2, result_1 = 0.

La raison 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 result_0 calcule d'abord )a * c(, évitant ainsi la perte de précision.

) 2.2 trop petit en magnitude

rouille

fn precision_test_decimals[test]( { let a: u128 = 10; let b: u128 = 3; let c: u128 = 4; let decimal: u128 = 100_0000;

let result_0 = a.checked_div)b(.unwrap)(.checked_mul)c(.unwrap)(;

let result_1 = a.checked_mul)decimal(.unwrap)(
                .checked_div)b(.unwrap)(
                .checked_mul)c(.unwrap)(
                .checked_div)decimal(.unwrap)(;

println!)"{}:{}", result_0, result_1(;
assert_eq!)result_0, result_1(;

}

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

![])https://img-cdn.gateio.im/webp-social/moments-7bdd27c1211e1cc345bf262666a993da.webp(

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

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

) 3.1 Ajustement de l'ordre des opérations

Faire en sorte que la multiplication entière ait la priorité sur la division.

3.2 augmenter l'ordre de grandeur des entiers

Utilisez une plus grande échelle pour créer de plus grosses molécules. Par exemple, définissez 1 NEAR = 10^24 yoctoNEAR.

3.3 perte de précision des calculs cumulés

Enregistrer et accumuler la perte de précision, compenser lors des calculs ultérieurs :

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() { let mut offset: u128 = 0; pour i dans 1..7 { offset = distribute[test]10_000_000_000_000_000_000_000_000, offset(; } }

![])https://img-cdn.gateio.im/webp-social/moments-1933a4a2dd723a847f0059d31d1780d1.webp(

) 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 haute précision et sans erreur d'arrondi.

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

Dans la conception des smart contracts, l'arrondi suit généralement le principe "à mon avantage" : si le plancher est à mon avantage, on arrondit vers le bas, si le plafond est à mon avantage, on arrondit vers le haut, et l'arrondi au plus proche est rarement utilisé.

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

EQ2%
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
LostBetweenChainsvip
· 07-25 17:53
Je ne peux même pas gérer les calculs entiers, qu'est-ce que je vais faire avec des contrats ?
Voir l'originalRépondre0
WagmiWarriorvip
· 07-25 11:45
Je suis arrivé au 7ème article, c'est détaillé.
Voir l'originalRépondre0
MetaMisfitvip
· 07-25 01:13
Rust cette smart contracts a trop de pièges, non ?
Voir l'originalRépondre0
DuckFluffvip
· 07-22 22:44
Eh bien, les nombres à virgule flottante causent encore des problèmes~
Voir l'originalRépondre0
MaticHoleFillervip
· 07-22 22:44
Venez témoigner des pièges de la précision !
Voir l'originalRépondre0
MetadataExplorervip
· 07-22 22:44
Les entiers sont souvent négligés, mais ils sont assez importants.
Voir l'originalRépondre0
MEVHuntervip
· 07-22 22:29
la précision est un pot de miel mev... garde tes floats serrés ou tu vas te faire rekt ser
Voir l'originalRépondre0
TrustlessMaximalistvip
· 07-22 22:16
Il faut éviter ce piège des opérations à virgule flottante.
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)