# Rustスマートコントラクト開発における数値計算精度の問題## 1. 浮動小数点演算の精度問題Rust言語は浮動小数点演算をネイティブにサポートしていますが、浮動小数点演算には避けられない計算精度の問題があります。スマートコントラクトを作成する際には、特に重要な経済/金融の決定に関わる比率や金利を扱う場合、浮動小数点演算の使用は推奨されません。Rust の倍精度浮動小数点型 f64 は、IEEE 754 標準に準拠し、基数 2 の科学的記数法を使用して表されます。 小数点以下の桁数では、小数点以下の桁数が有限の浮動小数点数では正確に表現できないものがあり、「丸め」という現象があります。例えば、NEARブロックチェーン上で10人のユーザーに0.7NEARトークンを配布する場合:さび#[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であり、正確な0.7ではないことを示しています。除算の結果も期待される0.07ではなく、不正確な0.06999999999999999に変わっています。この問題を解決するために、定点数の使用を検討できます。NEAR Protocolでは、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_000, "");}これにより正確な計算結果が得られます: 0.7 NEAR / 10 = 0.07 NEAR。! [](https://img-cdn.gateio.im/social/moments-7bdd27c1211e1cc345bf262666a993da)## 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).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,");}実行結果はresult_0とresult_1が等しくないことを示しています。理由は整数の除算が除数未満の精度を切り捨てるためです。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).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, "");}結果はresult_0=12,result_1=13を示しており、実際の期待値は13.3333....であるべきです。! [](https://img-cdn.gateio.im/social/moments-1933a4a2dd723a847f0059d31d1780d1)## 3. 数値精算のためのRustスマートコントラクトの書き方精度を向上させるために、以下の防護手段を講じることができます:### 3.1 操作の順序を調整する整数の乗算を整数の除算より優先します。### 3.2 整数の数量を増やすより大きな数量を使用して、より大きな分子を作成します。例えば、5.123 NEARを表すには、5.123 * 10^10 = 51_230_000_000を使用して、次の計算に参加します。### 3.3 運用精度の累積損失避けられない精度の問題について、累積した計算精度の損失を記録することができます。例えば:さび定数 USER_NUM: u128 = 3;fn distribute(amount: u128, オフセット: u128) -> u128 { token_to_distribute = オフセット + 金額とします。 per_user_share = token_to_distribute / USER_NUMとします。 println!("per_user_share {}", per_user_share); recorded_offset = token_to_distribute - per_user_share * USER_NUM; recorded_offset}#[test]fn record_offset_test() { mutオフセットをしましょう:u128 = 0; for i in 1..7 { println!("ラウンド {}", i); オフセット = distribute(to_yocto("10"), offset); println!("オフセット {}\n", offset); }}この方法では、毎回配布される残りのトークンを蓄積し、次回の配布時に一緒に配布することで、最終的に十分な配布を達成します。### 3.4 では、Rust Crate ライブラリ rust-decimal を使用しますこのライブラリは、有効な精度計算と丸め誤差のない小数の金融計算を必要とする場合に適しています。### 3.5 丸め機構を考慮するスマートコントラクトの設計において、丸め問題は通常「私は得をして、他人には私の羊毛を刈らせない」という原則を採用します。状況に応じて切り下げまたは切り上げを選択し、四捨五入はほとんど使用されません。! [](https://img-cdn.gateio.im/social/moments-6e8b4081214a69423fc7ae022d05c728)
Rustスマートコントラクト開発:数値計算精度の難題を克服する
Rustスマートコントラクト開発における数値計算精度の問題
1. 浮動小数点演算の精度問題
Rust言語は浮動小数点演算をネイティブにサポートしていますが、浮動小数点演算には避けられない計算精度の問題があります。スマートコントラクトを作成する際には、特に重要な経済/金融の決定に関わる比率や金利を扱う場合、浮動小数点演算の使用は推奨されません。
Rust の倍精度浮動小数点型 f64 は、IEEE 754 標準に準拠し、基数 2 の科学的記数法を使用して表されます。 小数点以下の桁数では、小数点以下の桁数が有限の浮動小数点数では正確に表現できないものがあり、「丸め」という現象があります。
例えば、NEARブロックチェーン上で10人のユーザーに0.7NEARトークンを配布する場合:
さび #[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であり、正確な0.7ではないことを示しています。除算の結果も期待される0.07ではなく、不正確な0.06999999999999999に変わっています。
この問題を解決するために、定点数の使用を検討できます。NEAR Protocolでは、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_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とresult_1が等しくないことを示しています。理由は整数の除算が除数未満の精度を切り捨てるためです。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....であるべきです。
!
3. 数値精算のためのRustスマートコントラクトの書き方
精度を向上させるために、以下の防護手段を講じることができます:
3.1 操作の順序を調整する
整数の乗算を整数の除算より優先します。
3.2 整数の数量を増やす
より大きな数量を使用して、より大きな分子を作成します。例えば、5.123 NEARを表すには、5.123 * 10^10 = 51_230_000_000を使用して、次の計算に参加します。
3.3 運用精度の累積損失
避けられない精度の問題について、累積した計算精度の損失を記録することができます。例えば:
さび 定数 USER_NUM: u128 = 3;
fn distribute(amount: u128, オフセット: u128) -> u128 { token_to_distribute = オフセット + 金額とします。 per_user_share = token_to_distribute / USER_NUMとします。 println!("per_user_share {}", per_user_share); recorded_offset = token_to_distribute - per_user_share * USER_NUM; recorded_offset }
#[test] fn record_offset_test() { mutオフセットをしましょう:u128 = 0; for i in 1..7 { println!("ラウンド {}", i); オフセット = distribute(to_yocto("10"), offset); println!("オフセット {}\n", offset); } }
この方法では、毎回配布される残りのトークンを蓄積し、次回の配布時に一緒に配布することで、最終的に十分な配布を達成します。
3.4 では、Rust Crate ライブラリ rust-decimal を使用します
このライブラリは、有効な精度計算と丸め誤差のない小数の金融計算を必要とする場合に適しています。
3.5 丸め機構を考慮する
スマートコントラクトの設計において、丸め問題は通常「私は得をして、他人には私の羊毛を刈らせない」という原則を採用します。状況に応じて切り下げまたは切り上げを選択し、四捨五入はほとんど使用されません。
!