# Rustスマートコントラクト開発日記 (7) 浮動小数点数と整数演算精度問題この記事では、Rustスマートコントラクトにおける浮動小数点数と整数の演算精度の問題、および数値精算のスマートコントラクトの作成方法について議論します。## 1. 浮動小数点演算の精度問題Rust言語は浮動小数点演算をネイティブでサポートしていますが、浮動小数点演算には避けられない計算精度の問題があります。スマートコントラクトを作成する際には、浮動小数点演算の使用は推奨されません。特に重要な経済/金融の意思決定に関わる比率や金利を扱う場合には注意が必要です。Rust言語では、浮動小数点数はIEEE 754標準を採用しており、底数2の科学的表記法で表されます。特定の小数(のように、0.7)は有限の長さの浮動小数点数で正確に表現できず、"丸め"現象が発生します。例えば、NEARブロックチェーン上で10人のユーザーに0.7NEARトークンを配布する場合:さび#[test]fn precision_test_float() { 量を仮定します:f64 = 0.7; 除数をしましょう:f64 = 10.0; let result_0 = amount / divisor; assert_eq!(result_0, 0.07, "");}実行結果は、amountの値が正確な0.7ではなく、近似値の0.69999999999999995559であることを示しています。除算の結果も正確ではなく、期待される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"); 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とします。 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 { オフセット = distribute(to_yocto("10"), offset); }}これにより、配布されなかったトークンを一時保管し、次回の配布時に一緒に配布することができます。### 3.4 では、Rust Crate ライブラリ rust-decimal を使用しますこのライブラリは、正確な精度計算と丸め誤差のない小数金融計算が必要な場合に適しています。### 3.5 丸め機構を考慮するスマートコントラクト設計時、通常は「私は得をしたい、他人が私の羊毛を刈ってはいけない」という原則を採用します。状況に応じて切り捨てまたは切り上げを選択し、四捨五入を使用することは非常に少ないです。! [](https://img-cdn.gateio.im/social/moments-6e8b4081214a69423fc7ae022d05c728)
Rust Smart Contractにおける数値精度問題と最適化解
Rustスマートコントラクト開発日記 (7) 浮動小数点数と整数演算精度問題
この記事では、Rustスマートコントラクトにおける浮動小数点数と整数の演算精度の問題、および数値精算のスマートコントラクトの作成方法について議論します。
1. 浮動小数点演算の精度問題
Rust言語は浮動小数点演算をネイティブでサポートしていますが、浮動小数点演算には避けられない計算精度の問題があります。スマートコントラクトを作成する際には、浮動小数点演算の使用は推奨されません。特に重要な経済/金融の意思決定に関わる比率や金利を扱う場合には注意が必要です。
Rust言語では、浮動小数点数はIEEE 754標準を採用しており、底数2の科学的表記法で表されます。特定の小数(のように、0.7)は有限の長さの浮動小数点数で正確に表現できず、"丸め"現象が発生します。
例えば、NEARブロックチェーン上で10人のユーザーに0.7NEARトークンを配布する場合:
さび #[test] fn precision_test_float() { 量を仮定します:f64 = 0.7;
除数をしましょう:f64 = 10.0;
let result_0 = amount / divisor;
assert_eq!(result_0, 0.07, ""); }
実行結果は、amountの値が正確な0.7ではなく、近似値の0.69999999999999995559であることを示しています。除算の結果も正確ではなく、期待される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とします。 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 { オフセット = distribute(to_yocto("10"), offset); } }
これにより、配布されなかったトークンを一時保管し、次回の配布時に一緒に配布することができます。
3.4 では、Rust Crate ライブラリ rust-decimal を使用します
このライブラリは、正確な精度計算と丸め誤差のない小数金融計算が必要な場合に適しています。
3.5 丸め機構を考慮する
スマートコントラクト設計時、通常は「私は得をしたい、他人が私の羊毛を刈ってはいけない」という原則を採用します。状況に応じて切り捨てまたは切り上げを選択し、四捨五入を使用することは非常に少ないです。
!