ライブラリ: 数学ユーティリティ - ページ 5

 
FXAI #:
これらは、浮動小数点数の比較や丸め、お金の書式設定に便利な3つの関数です:

1. `bool DoubleEquals(double x, double y, double eps)` は、2つのダブル値 `x` と `y` を与えられたイプシロン値 `eps` で比較し、それらが与えられた許容範囲内で等しいかどうかを示すブール値を返す。

2. `double RoundTo(double value, int digits)` 二重の値 `value` を、与えられた小数 `digits` の数に丸める。

3.3. `string FormatMoney(double amount)` は、double 値 `amount` を通貨額を表す文字列としてフォーマットする。これは、小数点以下2桁で金額をフォーマットし、小数点をカンマに置き換え、読みやすくするために3桁ごとに空白を挿入する。また、最後に `AccountInfoString(ACCOUNT_CURRENCY)` から取得した通貨記号を追加する。

本当にありがとうございます。しかし、これらの関数はすでにライブラリに実装されています(あなたのものよりも堅牢な結果でさえあります)。

// 2つの数値が "n "桁の精度まで等しいかどうかをチェックする。
int    Compare(const double a, const double b, const int digits);
bool   EqualDoubles(double a, double b, int significantDigits = 15);
bool   IsClose(const double a, const double b, const int maxDifferentSignificantDigits = 2)

// 予期せぬ結果を避けるために、正確な小数の丸めを行う。
double Round(const double v);                       
double Round(const double value, const int digits); 
double Round(const double value, const double step);

// 数千の区切り文字と指定された小数を持つ double をフォーマットする。
string FormatDouble(const double number, const int digits, const string separator=",");

 

こんにちは、 @amrali さん

これはバグでしょうか?

私は2番目の印字が "0.0001 "になると思っていました。

もしバグだとしたら、どのように修正すればよいでしょうか?そうでない場合、私のコードのどこが悪いのでしょうか?

ありがとうございました。

double ask = 1.2973;
double bid = 1.2972;
double spread = ask - bid;

Print(spread);// 出力0.00009999999999998899
Print(StripError(spread));// 出力0.000099999999999989


amrali
amrali
  • 2024.04.05
  • www.mql5.com
Trader's profile
 
jonlinper #:

こんにちは、 @amraliさん

これはバグでしょうか?

私は2番目の印字が "0.0001 "になると思っていました。

もしバグだとしたら、どのように修正すればいいのでしょうか?そうでない場合、私のコードのどこが悪いのでしょうか?

ありがとうございました。


16進数表現をプリントすれば、スプレッドが真の実数値0.0001からかけ離れていることがわかるでしょう(引き算の際の丸め誤差が原因です)。

そのため、四捨五入の手順を使用する必要があります。

   double ask = 1.2973;
   double bid = 1.2972;
   double spread = ask - bid;

   Print(spread);                                  // 出力0.00009999999999998899
   Print(StripError(spread));                      // 出力0.000099999999999989

   Print(DoubleToHexadecimal(spread));             // 出力0x3F1A36E2EB1C4000
   Print(DoubleToHexadecimal(StripError(spread))); // 出力0x3F1A36E2EB1C4001
   Print(DoubleToHexadecimal(0.0001));             // 出力:0x3F1A36E2EB1C432D

   Print(EQ(spread, 0.0001));                      // 出力:true
   Print(Round(spread, 16));                       // 出力0.001

注意すべき点がいくつかあります:

StripError()は 16桁目の 0.0000999999998899で 丸める(0はカウントされない)。

Round(x, 16)小数点 以下16桁目の0.0000999999998899で 丸めます。

 
jonlinper #: 私は2番目の印字が「0.0001」になると思っていた。

浮動小数点には無限の小数点がある。浮動小数点と、正確に表現できない数値があることを理解していないのはあなた です。(1/10とか)
倍精度浮動小数点フォーマット - Wikipedia

オペランド == も参照してください。- MQL4プログラミング・フォーラム 2013年)


marketinfo() の decima に関する質問 - MQL4 プログラミングフォーラム (2016)

 
William Roeder #:

浮動小数点には無限の小数点がある。浮動小数点を理解していない、正確に表現できない数字があることを理解していないのはあなた です。(1/10とか)
倍精度浮動小数点フォーマット - Wikipedia

オペランド == も参照してください。- MQL4プログラミング・フォーラム 2013年)


marketinfo()のdecimaに関する質問 - MQL4プログラミングフォーラム (2016)

親愛なるウィリアム、ご説明ありがとうございます。しかし、「無限の小数」に関しては同意できません。FP数値の小数点以下の桁数は有限です。(例えば、0.1の場合、小数点以下の桁数はちょうど52桁です)。

私のライブラリのDoubleToStringExact(0.1) を使えば、小数点以下の桁数をすべて表示できます。また、この電卓を使って完全な小数点の文字列をチェックすることもできます: https://www.exploringbinary.com/floating-point-converter/
また、完全な10進数文字列は常に桁 "5 "で終わっていなければならないことに注意してください。

0.1000000000000000055511151231257827021181583404541015625
 

倍数で有効数字のみを印刷する最も最適な方法は何ですか?

double Trunc(const double value, const int digits);

この関数は99,9%の数値ではうまくいきますが、1.0000000000のような丸い数値では 問題があります。

私の問題は、有効数字以外の桁を取り除く必要があることです、

のようなものを使うことにしました:

string Normalize_Double_ToString(double n, int d)
{
   // ステップ1 - 末尾のゼロを除外するのに役立つ
   n = Round(n, d);

   // ステップ2 - 小数の有効数字を数える
   int sd = GetSignificantDecimals(n);

   // ステップ3 - @dで指定された以上のものはいらない
   if (sd > d){ sd = d; }

   // ステップ4 - 負のランダムな丸めをせずに、不要な小数を削除する。 
   double t = Trunc(n, sd);

   // デバッグ
   //PrintFormat("%s [%d] [%d] :: %s", DoubleToString(n, DBL_DIG), d, sd, DoubleToString(t, sd));

   // ステップ 5 - 精度の設定
   string s = DoubleToString(t, sd);

   return s;
}

しかし、1.00000000のような丸い数字で、最適化された最小の文字列を得ることができるのでしょうか?

ありがとうございました。

 

私が使っているのは

int GetSignificantDecimals(double value)

あなたの

int GetSignificantDigits(double value)

を使用していることに気づきました。

int GetSignificantDecimals(double value)
{
   if(!value || !MathIsValidNumber(value))
   {
      return 0;
   }

   // 小数の和
   int digits = GetDigits(value);

   // 末尾のゼロを除く
   while(MathMod(value, 10) == 0)
   {
      digits--;
   }

   return digits;
}
 
Cristian Dan Fechete 丸い数値には 問題があります。

私の問題は、有効数字以外を取り除く必要があるのですが、なぜか@Truncだけではできません、

のようなものを使うことにした:

しかし、1.00000000のような丸い数字で、最適化されて最小の文字列を得ることができるかどうか疑問に思っていました。

ありがとうございました。

申し訳ありませんが、あなたのコードは基本的な概念を混乱させているので、有効数字とは何かを理解する必要があります。
何をしようとしているのか...コードではなく、わかりやすい言葉で説明してください。何が間違っているのか、何を期待しているのか、例を挙げてください。
 
amrali #:
申し訳ありませんが、あなたのコードは基本的な概念を混乱させていると思いますので、有効数字とは何かを理解する必要があります。
何をしようとしているのか...コードではなく、わかりやすい言葉で説明してください。何が間違っているのか、何を期待しているのか、例を挙げてください。

お時間を割いていただきありがとうございます。

私は基本的に、可能な限り短い数字を「印刷」する必要があります。例えば

1.0000000 -> 1

1.0090000 -> 1.009

123.00100 -> 123.001

私にとっての「重要な桁」とは、「取り除くと数値の値が変わる桁」のことで、末尾のゼロは重要ではありません。


ところで、Windowsの最後のアップデート以降、関数Round(double, int)がMT4をブロックしています。最初に投稿したコードは完璧に動作していたのですが、昨日の夜からMT4クライアントが完全にフリーズしてしまいました。

 
Cristian Dan Fechete #:

お時間を割いていただきありがとうございます。

私は基本的に、可能な限り短い数字を「印刷」する必要があります。例えば

1.0000000 -> 1

1.0090000 -> 1.009

123.00100 -> 123.001

私にとっての "有効数字 "とは、"取り除いた場合に数字の値が変わる数字 "という意味であり、末尾のゼロは有効数字ではない。


ところで、Windowsの最後のアップデート以来、関数Round(double, int)がMT4をブロックする原因となっています。最初に投稿したコードは完璧に動作していたのですが、昨日の夜からMT4クライアントが完全にフリーズしてしまいました。

Print()関数や、(string)dblのようにdoubleを文字列にキャストすると、有効数字の最短桁数を得ることができます。これはMQLの組み込み機能です。(私はすでに開発チームに修正を提案し、コードにマージされました)。

必要なのは
string num_str = string(number).
またはPrint(number);

そのため、数値を可能な限り短い文字列に印刷またはフォーマットするための専用関数はライブラリ内部に必要ありません。

小数点以下の桁数を制御する必要がある場合にのみ、DoubleToString()を使用してください。digits パラメータが数値の小数点以下の桁数よりも大きい場合、返される文字列に 0 が追加されます。
DoubleToString(1.09, 5)は、"1.09000 "という文字列を返します。

digitsパラメータが数値の小数点以下の桁数より小さい場合、数値は近似されます。このような混乱は、数値と文字列の区別がつかないために起こります。

1.09、1.090、1.0900、1.09000、1.090000のようなゼロで終わる数字は、同じ倍精度fp数として変数に格納されることに注意してください。これらは、手動入力としてユーザーが直接入力することでしか実現できません。プログラム内部では、これらの数値はすべて1.09という同じ数値として格納され、末尾の0は格納されません。

double a = 1.09;
double b = 1.090000;
Print(a); // "1.09".
Print(b); // "1.09"

round、ceil、floorのような丸め関数は、入力された数値を、指定された小数点以下の桁数を持つ最も近い倍精度数値、またはRoundToSignificantDigits()の場合は指定された有効数字の合計を含む数値に変更(近似)します。

倍精度 fp 数値を文字列に変換する際の混乱が解消されることを願っています。