# 錆びたスマートコントラクト栽培日記 (7):整数計算精度問題過去の振り返り:* Rustスマートコントラクト養成日記(1)合約状態データ定義とメソッド実装* Rustスマートコントラクト養成日記(2)編写Rustスマートコントラクト単体テスト* Rustスマートコントラクト育成日記(3)Rustスマートコントラクトのデプロイ、関数呼び出しおよびExplorerの使用* Rustスマートコントラクト育成日記(4)Rust スマートコントラクト整数オーバーフロー* Rustスマートコントラクト養成日記(5)重入攻撃* Rustスマートコントラクト養成日記(6)拒絶サービス攻撃## 1. 浮動小数点演算の精度問題Solidityとは異なり、Rustはネイティブで浮動小数点演算をサポートしています。しかし、浮動小数点演算には避けられない精度の問題があるため、特に重要な経済/金融の決定に関わる比率や金利を扱う際には、スマートコントラクトでの使用は推奨されません。Rust は、浮動小数点数の表現に関する IEEE 754 標準に準拠しています。 f64 倍精度浮動小数点型は、コンピューターでは 2 進数科学表記法によって内部的に表されます。いくつかの小数は有限桁数の二進数で正確に表現できます。例えば、0.8125は0.1101として表現できます。しかし、0.7のような小数は無限に循環する二進数表現が生成され、有限桁の浮動小数点数で正確に表現することができず、"丸め"の問題が存在します。NEARブロックチェーン上で10人のユーザーに0.7 NEARトークンを配布する例:さび#[test]fn precision_test_float() { 量を仮定します:f64 = 0.7; 除数をしましょう:f64 = 10.0; let result_0 = amount / divisor; println!("量の値: {:.20}", amount); assert_eq!(result_0, 0.07);}実行結果は、amountの実際の値が0.69999999999999995559であり、result_0が0.06999999999999999であり、期待される0.07とは等しくないことを示しています。この問題を解決するために、定点数を使用できます。NEARでは、通常1つのNEARトークンを10^24のyoctoNEARで表します。修正されたコード:錆 #[test]fn precision_test_integer() { 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; assert_eq!(result_0, 70_000_000_000_000_000_000_000);}こうすることで正確な結果が得られます:0.7 NEAR / 10 = 0.07 NEAR。## 2. Rustの整数計算の精度に関する問題整数演算は特定のシナリオで浮動小数点精度の問題を解決できますが、整数計算にも精度の問題があります。### 2.1 操作の順序同じレベルの乗除法では、順序の変更が結果に影響を与える可能性があります:さび#[test]fn precision_test_div_before_mul() { Aを仮定します:U128 = 1_0000; Bを仮定します:U128 = 10_0000; Cを仮定します:U128 = 20; let result_0 = a.checked_mul(c).unwrap().checked_div(b). unwrap(); result_1 = a.checked_div(b).unwrap().checked_mul(c). unwrap(); assert_eq!(result_0,result_1);}結果は、result_0 = 2、result_1 = 0を示しています。理由は整数除算が除数未満の精度を切り捨てるからです。result_1を計算する際、(a / b)は最初に精度を失い0になります。一方、result_0は先に(a * c)を計算することで精度の損失を避けました。### 2.2 小さすぎる数量さび#[test]fn precision_test_decimals() { Aを仮定します:U128 = 10; Bを仮定します:u128 = 3; C:u128 = 4とします。 小数で仮定します:u128 = 100_0000; let result_0 = a.checked_div(b).unwrap(928374656574839201.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(;}結果はresult_0 = 12, result_1 = 13を示しており、後者は実際の値13.3333により近い。! [])https://img-cdn.gateio.im/social/moments-7bdd27c1211e1cc345bf262666a993da(## 3. 数値精算のためのRustスマートコントラクトの作成方法精度を向上させるために、以下の対策を講じることができます:) 3.1 操作の順序を調整する整数の乗算を除算より優先させる。### 3.2 整数の数の桁を増やすより大きな数量を使用して、より大きな分子を作成します。例えば、1 NEAR = 10^24 yoctoNEARと定義します。### 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}#)fn record_offset_test[test]( { mutオフセットをしましょう:u128 = 0; 1から7までのiについて{ オフセット = distribute)10_000_000_000_000_000_000_000_000, offset(; }}! [])https://img-cdn.gateio.im/social/moments-1933a4a2dd723a847f0059d31d1780d1() 3.4 では、Rust Crate ライブラリ rust-decimal を使用しますこのライブラリは、高精度で丸め誤差のない小数金融計算を必要とする場合に適しています。### 3.5 丸め機構を考慮するスマートコントラクト設計において、丸めは通常「私に有利な」原則に従います: 下に切り捨てるのが有利であれば下に、上に切り上げるのが有利であれば上に、四捨五入はほとんど使用されません。! []###https://img-cdn.gateio.im/social/moments-6e8b4081214a69423fc7ae022d05c728(
Rustスマートコントラクト整数計算精度問題及び解決策
錆びたスマートコントラクト栽培日記 (7):整数計算精度問題
過去の振り返り:
1. 浮動小数点演算の精度問題
Solidityとは異なり、Rustはネイティブで浮動小数点演算をサポートしています。しかし、浮動小数点演算には避けられない精度の問題があるため、特に重要な経済/金融の決定に関わる比率や金利を扱う際には、スマートコントラクトでの使用は推奨されません。
Rust は、浮動小数点数の表現に関する IEEE 754 標準に準拠しています。 f64 倍精度浮動小数点型は、コンピューターでは 2 進数科学表記法によって内部的に表されます。
いくつかの小数は有限桁数の二進数で正確に表現できます。例えば、0.8125は0.1101として表現できます。しかし、0.7のような小数は無限に循環する二進数表現が生成され、有限桁の浮動小数点数で正確に表現することができず、"丸め"の問題が存在します。
NEARブロックチェーン上で10人のユーザーに0.7 NEARトークンを配布する例:
さび #[test] fn precision_test_float() { 量を仮定します:f64 = 0.7;
除数をしましょう:f64 = 10.0;
let result_0 = amount / divisor;
println!("量の値: {:.20}", amount); assert_eq!(result_0, 0.07); }
実行結果は、amountの実際の値が0.69999999999999995559であり、result_0が0.06999999999999999であり、期待される0.07とは等しくないことを示しています。
この問題を解決するために、定点数を使用できます。NEARでは、通常1つのNEARトークンを10^24のyoctoNEARで表します。修正されたコード:
錆
#[test] fn precision_test_integer() { 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; assert_eq!(result_0, 70_000_000_000_000_000_000_000); }
こうすることで正確な結果が得られます:0.7 NEAR / 10 = 0.07 NEAR。
2. Rustの整数計算の精度に関する問題
整数演算は特定のシナリオで浮動小数点精度の問題を解決できますが、整数計算にも精度の問題があります。
2.1 操作の順序
同じレベルの乗除法では、順序の変更が結果に影響を与える可能性があります:
さび #[test] fn precision_test_div_before_mul() { Aを仮定します:U128 = 1_0000; Bを仮定します:U128 = 10_0000; Cを仮定します:U128 = 20;
}
結果は、result_0 = 2、result_1 = 0を示しています。
理由は整数除算が除数未満の精度を切り捨てるからです。result_1を計算する際、(a / b)は最初に精度を失い0になります。一方、result_0は先に(a * c)を計算することで精度の損失を避けました。
2.2 小さすぎる数量
さび #[test] fn precision_test_decimals() { Aを仮定します:U128 = 10; Bを仮定します:u128 = 3; C:u128 = 4とします。 小数で仮定します:u128 = 100_0000;
}
結果はresult_0 = 12, result_1 = 13を示しており、後者は実際の値13.3333により近い。
! [])https://img-cdn.gateio.im/webp-social/moments-7bdd27c1211e1cc345bf262666a993da.webp(
3. 数値精算のためのRustスマートコントラクトの作成方法
精度を向上させるために、以下の対策を講じることができます:
) 3.1 操作の順序を調整する
整数の乗算を除算より優先させる。
3.2 整数の数の桁を増やす
より大きな数量を使用して、より大きな分子を作成します。例えば、1 NEAR = 10^24 yoctoNEARと定義します。
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 }
#) fn record_offset_test[test]( { mutオフセットをしましょう:u128 = 0; 1から7までのiについて{ オフセット = distribute)10_000_000_000_000_000_000_000_000, offset(; } }
! [])https://img-cdn.gateio.im/webp-social/moments-1933a4a2dd723a847f0059d31d1780d1.webp(
) 3.4 では、Rust Crate ライブラリ rust-decimal を使用します
このライブラリは、高精度で丸め誤差のない小数金融計算を必要とする場合に適しています。
3.5 丸め機構を考慮する
スマートコントラクト設計において、丸めは通常「私に有利な」原則に従います: 下に切り捨てるのが有利であれば下に、上に切り上げるのが有利であれば上に、四捨五入はほとんど使用されません。
! []###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp(