私たちは皆、学校で浮動小数点数について学びました。小数点のない整数とは対照的です。
数字の 5 が整数で、5.43 が浮動小数点数であることを知るのに、数学の達人である必要はありません。明らかに、私たちは皆、この 2 つを明確に区別する点である小数点に精通しています。
もちろん、float がプログラミング言語で使用されており、日々の計算に必要であるため、明確なプリミティブ データ型として分類されていることは驚くことではありません。
Python または Java の学習を開始するときは、float データ型を使用します。特に、2 つの浮動小数点数自体の間で除算演算を実行する場合。
Python では、浮動小数点数を宣言する必要がない場合でも、これらの数値を 2 つ除算すると、以下に示すように同じデータ型の数値になる可能性があります。
使用されるすべての変数に対して「float」プリミティブ データ型を明示的に宣言する必要がある場合でも、Java でも同じ結果を生成できます。
おわかりのように、この種の計算は多くのアプリケーションで必要であり、これらのプログラミング言語の両方で浮動小数点数を使用した演算が機能として利用できるのはまさにそのためです。
それとはまったく異なり、Solidity を学習している場合は、Solidity には浮動小数点数のデータ型が含まれていないことに気付くはずです。
代わりに、Solidity でコードを記述した場合は、符号付きまたは符号なしの整数型の数値を使用することになります。
したがって、前のセクションで示したのと同じ計算 (5 割る 2) を実行する場合、スマート コントラクトの一部として次のようになります。
ただし、Solidity は float データ型をサポートしていないため、得られる結果は同じではありません。少なくとも、まだです。
具体的には、Solidity は結果をゼロに丸めます。この場合、上に示したように、値は 2 になります。これを両方の数値の剰余演算と考えてください。
通常の状況では、これはあまり問題になりませんが、結果が計算のエラーにつながる場合があります。そのため、除算操作をできるだけ回避または延期することをお勧めします。
学校の BODMAS 規則では、すべての数学の生徒が乗算の前に除算を実行する必要がある場合でも、Solidity で整数除算を実行する必要がある場合、これはお勧めできません。
この簡単な例で、 が 3 つの数値で乗算と除算を実行する理由を調べてみましょう。
スマート コントラクトをデプロイするときに 1、3、5 の数字を入力すると、getResult 関数と getResult2 関数で同じ値が得られるはずですよね?
単純な計算機を使用すると、float 値がないため、Solidity では 1.666 の float 値が得られるはずです。
残念ながら、以下に示すように、getResult および getResult2 関数の結果を確認すると、これは起こりません。
最初に除算を実行すると、最終結果はゼロになります。 1 の期待値とは対照的に、getResult 関数でその操作を最後まで延期した場合。
おわかりのように、float 値がないことを予測してこの値を計算したとしても、精度の低下を引き起こす可能性のあるエラーが発生します。これは、簡単に回避できる経済的損失につながる可能性があります。
では、整数除算エラーを防ぐにはどうすればよいでしょうか。さらに重要なことは、計算の精度を上げるにはどうすればよいでしょうか?これを行う最も一般的な 3 つの方法を見てみましょう。
このエラーを回避する方法はいくつかあるため、最も単純な修正方法から始めて、もう 2 つ見てから終わりにしましょう。
方法 #1: 乗数を使用する
ここで、除算演算を最後に配置するとともに、エラーや不正確な値にならないようにする 1 つの方法は、乗数を使用することです。以下の例では、前に使用したのと同じ 3 つの数値と共に 100 の乗数を使用します。
ここで、次のコードを使用してコントラクトをデプロイし、両方の関数を呼び出すと、次の出力が得られます。
目的の出力は 1.666 または 166/100 であるため、乗数が 3 つの数値と連携して機能する場合、getResult2 の値が必要な精度を提供することがわかります。もちろん、getResult 関数のように乗数を使用しない場合は、1 が得られます。
0.666 は、Solidity を使用するときにデフォルトで期待されるように、結果から切り捨てられます。したがって、この値を取得するには、結果を乗数で割るだけです。
ご存知かもしれませんが、Solidity は、符号付き整数と符号なし整数の両方の場合に四捨五入するとゼロに向かって移動するため、この修正は符号付き整数の場合にも機能します。引数マイナス 1 を使用して以下のコードをデプロイして実行すると、 、3 と 5:
符号付き整数の関数によって生成される値については、次のとおりです。
明らかに、乗数を使用して、符号付き整数の精度も維持できます。ただし、次に説明する符号付き整数を丸める正確な方法があります。
方法 #2: 符号付き整数に床除算を使用する
さて、下の数直線を見ると、2 つの符号なし整数の間で整数除算を実行した結果は、0 に近づくように丸められます。 1.666 を取得する場合と同様に、Solidity はそれを小さい数である 1 に丸めます。
ただし、符号付き整数の場合、-1.6666 の結果は 2 つの数値のうち大きい方の -1 に丸められます。したがって、Solidity でデフォルトで実装されているゼロに丸められた除算とは対照的に、フロア除算をここで適用する必要があります。もちろん、精度のために。
float データ型が使用可能な場合、-1.666 の値が計算されます。 Solidity はこれを -1 に切り捨てますが、符号付き整数に床除算を適用すると -2 に減ります。
getResult および getResult2 関数を呼び出すと、以下に示すように、引数から 1、3、および 5 を引いた値が取得されます。
おわかりのように、getResult はゼロ方向への丸めアプローチを使用して値を計算しますが、getResult2 は最小の整数に四捨五入します。
方法 #3: ABDKMath64x64 ライブラリを使用する
ここで、最後の方法として、除算演算の結果を固定小数点数に変換する ABDKMath64x64 ライブラリを使用します。
繰り返しになりますが、このライブラリを使用すると、Solidity でデフォルトで使用できるゼロ方向への丸め方法とは対照的に、精度が向上すると言われています。出力を理解するために、以下に示すように、add の結果を div 関数と比較してみましょう。
スマート コントラクトをデプロイするときに引数 1、1、および 1 を追加すると、以下に示すように値が取得されます。
add 関数が 2 の整数値を返し、3 つの引数を合計するとそれだけの値になることは驚くべきことではありません。 div 関数に関しては、符号付き 64.64 ビット固定小数点数を表し、通常の数値演算を実行できる int 128 値が返されます。
もちろん、ABDKMath64x64 ライブラリは、整数除算エラーを防ぐこと以外に精度を向上させるために使用できる唯一のものではありません。
Fixidity 、 DSMath 、 BANKEXライブラリなど、異なる数値形式を使用するいくつかの例があります。上記の例で使用されている 64.64 ビットの固定小数点数形式とは異なる数値形式。したがって、これらのライブラリは探索するのに役立つように見えるかもしれませんが、それらの数値形式は利用可能な他のライブラリでは機能しないことに注意してください。