result_0 = (a / b) * c
let result_0 = a.checked_div(b).expect("ERR_DIV").checked_ mul(c).expect("ERR_MUL");
result_1 = (a * 小数 / b) * c / 小数;
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");
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.
Rustスマートコントラクト数値精算:避坑ガイドとベストプラクティス
Rustスマートコントラクト中的数値精算
スマートコントラクトプログラミングにおいて、数値計算の精度の問題は特に重要です。本稿では、Rustスマートコントラクトにおける一般的な数値精算の問題とその解決策について探討します。
1. 浮動小数点演算の精度問題
Rust言語は浮動小数点数の演算をネイティブにサポートしていますが、浮動小数点数の演算には避けられない計算精度の問題があります。重要な経済/金融の決定に関わる比率や利率を扱う際には、浮動小数点数の演算を使用することは推奨されません。
Rust言語の倍精度浮動小数点型f64はIEEE 754標準に従い、底数2の科学的表記法を採用しています。特定の小数(のように0.7)は有限のビット長の浮動小数点数で正確に表現することができず、"丸め"現象が存在します。
例えば、NEARブロックチェーン上で10人のユーザーに0.7 NEARトークンを配布する場合:
さび 量を仮定します:f64 = 0.7;
除数をしましょう:f64 = 10.0;
let result_0 = amount / divisor;
amountの実際の値は0.69999999999999995559であり、result_0の結果は0.06999999999999999で、期待される0.07ではありません。
この問題を解決するために、定点数表現を使用することを考慮できます。NEAR Protocolでは、通常10^24を分母として使用します。つまり、1 NEAR = 10^24 yoctoNEAR。修正された計算方法は以下の通りです:
さび N: u128 = 1_000_000_000_000_000_000_000_000_000_000_000;
量を仮定します: U128 = 700_000_000_000_000_000_000_000_000; 除数をしましょう:u128 = 10;
let result_0 = amount / divisor;
こうすることで正確な計算結果が得られます: 0.7 NEAR / 10 = 0.07 NEAR。
!
2. Rustの整数計算の精度に関する問題
整数計算を使用することは、特定のシナリオにおける浮動小数点精度の問題を解決することができますが、計算精度に影響を与える要因がいくつか存在します。
2.1 操作の順序
同じ算術優先順位の掛け算と割り算では、その前後の順序の変化が計算結果に直接影響を与える可能性があります。例えば:
さび Aを仮定します:U128 = 1_0000; Bを仮定します:U128 = 10_0000; 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");
result_0とresult_1の計算結果が異なる理由は、整数除算が除数より小さい精度を切り捨てるためです。
2.2 小さすぎる数量
小さなスケールの計算に関しては、精度の問題を引き起こす可能性もあります:
さび Aを仮定します:U128 = 10; Bを仮定します:u128 = 3; C:u128 = 4とします。 小数で仮定します: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 * 小数 / b) * c / 小数;
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");
result_0とresult_1の結果は異なり、result_1は実際の期待値に近くなります。
!
3. 数値精算のRustスマートコントラクトの書き方
Rustスマートコントラクトにおける数値計算の精度を向上させるために、以下の対策を講じることができます:
3.1 操作の順序を調整する
整数の乗法を整数の除法より優先させる。
3.2 整数の数量級を増やす
より大きな数量級を使用して、より大きな分子を生成し、計算精度を向上させます。
3.3 運用精度の累積損失
避けられない整数計算の精度問題については、累積された計算精度の損失を記録することを考慮できます。例えば:
さび 定数 USER_NUM: u128 = 3;
fn distribute(amount: u128, オフセット: u128) -> u128 { token_to_distribute = オフセット + 金額とします。 per_user_share = token_to_distribute / USER_NUMとします。 recorded_offset = token_to_distribute - per_user_share * USER_NUM; recorded_offset }
この方法は、複数回の配布において精度の損失を段階的に補償することができます。
3.4 では、Rust Crate ライブラリ rust-decimal を使用します
このライブラリは、有効な精度計算と丸め誤差のない小数の金融計算を必要とする場合に適しています。
3.5 丸め機構を考慮する
スマートコントラクトを設計する際、丸めの問題は通常「私は利益を得たい、他人は私の利益を奪ってはいけない」という原則に従います。状況に応じて切り捨て、切り上げ、または四捨五入を選択します。
これらの方法を採用することで、Rustスマートコントラクトにおける数値計算の精度と信頼性を大幅に向上させることができます。
!