価格 != 価格 ? - ページ 5

 

価格の等価性(というより倍率の等価性)について、こんな大前提から始めてみました -。

(P1) y = 1.50000 とすると、x が (i) 1.499995 以上かつ (ii) 1.500005 未満の任意の実数である限り、x == y である。

P1を踏まえて、私は次のように結論づけた。

(P2). y = 1.50000としたとき: a == y, b == y, aとbが(i) 1.499995以上, (ii) 1.500005未満の実数であれば、a == bとなる。

例としては、1.500055 == 1.50006, 1.500055 == 1.500064, 1.500051 != 1.500059, 1.500054 != 1.500056 などがあります。

以上のことから、(1)2つの価格を引数にとり、(2)それらの価格を最も近い点数に丸め、(3)それらの2つの価格が等しいかどうかを判断する関数(以下)を作成しました。

bool IsEqual(double price1, double price2) {
   // Price Conditioning
   //    * this fixes the occurrence of 1.5000551 != 1.5000550
   price1 += Point * 0.0015;
   price2 += Point * 0.0015;
      
   int p1 = MathRound(price1 / Point),
       p2 = MathRound(price2 / Point);
          
   return (p1 == p2);
}

この関数は単純明快なものですが、「価格の条件付け」の部分について少しコメントしておきます。 ご存知のように、倍精度浮動小数点形式の変数(すなわち。1.5000551と1.5000550を四捨五入して比較すると(それぞれ1.50006と1.50005)、上記のP1、P2では等しいはずなのに等しくないように見えます。 私は(何度かテストした結果)、リテラル1.5000550が変数に〜1.5000549999として格納されていると結論づけました。これを改善するために、価格が半値(x.xxxxx5)から1万分の15ポイント以内にあれば、直近のポイントに切り上げるための最低基準を満たしたと見なすことにしたのです。また、これらの値は、四捨五入の前提を増やしたり減らしたりするために調整することができます。

RaptorUKと WHRoeder(と他の人):上記を青写真として、私はRaptorUKの前の投稿に基づいて、ComparePrices()という次の関数を作りました

#define EQ      1
#define NEQ     2
#define LT      3
#define LToE    4
#define GT      5
#define GToE    6

bool ComparePrices(double FristPrice, double SecondPrice, int ComparisonType) {
   // Price Conditioning
   FirstPrice  += Point * 0.0015;
   SecondPrice += Point * 0.0015;
      
   int price1 = MathRound(FirstPrice / Point),
       price2 = MathRound(SecondPrice / Point);
                
   switch(ComparisonType) {
      case LToE: return (price1 < price2 || price1 == price2);
      case GToE: return (price1 > price2 || price1 == price2);
      case LT:   return (price1 < price2);
      case GT:   return (price1 > price2);
      case EQ:   return (price1 == price2);
      case NEQ:  return (price1 != price2);
      default:   return (false);
   }    
}

いつものように、有益な/建設的なコメントを歓迎します。:)

 

私自身、読みやすさとパフォーマンスの妥協点に到達するために、この問題に少し取り組んできました。


私は、eq(a,b), ne(a,b), lt(a,b) などの個々の関数に 落ち着きました。


Eg

if (eq(a,b)) { ...}


私の低速VMで4999999回の反復計算を行った場合のパフォーマンスについて、以下のようなベースライン測定結果が得られました。

空ループ:370ms

インラインMathAbs(a-b) < gHalfPoint (global) :2482ms

空のブール関数。4266ms <-- この数値になるべく近づけることを目標にしています。

私が管理した中で最も速いeq()の実装は以下の通りです。

インラインのMathsAbs()呼び出しより約2.3倍、trueを返すだけの空のboolean関数呼び出しより約1.3倍遅い。

あと余談ですが、MQLはブール式を短絡させないことを発見しました。

bool eq(double a,double b) {

   if (a > b) {
      return ((a-b) < gpoint2);
   } else {
      return ((b-a) < gpoint2);
   }

}

5558ms

また、グローバルよりもスタティックが好きな場合(すべてのコードを一箇所にまとめておくため)。

bool eq(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a > b) {
      return (a-b < p2);
   } else {
      return (b-a < p2);
   }
}

5718msで


eq()やne()はより複雑なので、lt()やgt()などはより速くなるはずです。

 
RaptorUK: では、TestValueが>でも<でもなく1.57373になるようにするにはどうしたらいいのでしょうか?

浮動小数点はある種の数に対して決して正確ではありません。

https://en.wikipedia.org/wiki/Floating_point

浮動小数点数は、ある整数を別の整数で割ることで表現できるため、有理数 です。例えば、1.45×103は(145/100)*1000または145000/100です。しかし、基数は表現できる分数を決定する。例えば、1/5は2進数では浮動小数点数として正確に表現できないが、10進数では正確に表現できる(0.2、つまり2×10-1)。しかし、1/3は2進数(0.010101...)でも10進数(0.333...)でも正確に表現できないが、3 進数では些細なこと(0.1または1×3-1)なのである。
だから私はNormalizeDoubleは絶対に使わないでくださいと言うのです。それはKludgeです。その使用は常に間違っています。
 
Thirteen:

case LToE: return (price1 < price2 || price1 == price2);
case GToE: return (price1 > price2 || price1 == price2);

ブローカーからの2倍値は1.2345750000000000から1.234584999999999まであり、それでも同じ1.23458の価格とみなされます。

しかし、あなたの関数は、1.2345750000000000は1.2345849999999のGToEではないと言っています。

1.23458499999999は1.23457500000000のLToEではありません。

比較にポイント/2を使用する必要がありますhttps://www.mql5.com/en/forum/136997/page3#780837

 
ydrol:

私自身も、読みやすさとパフォーマンスの妥協点を探るために、少しばかり工夫してみました。

0.0は特別なケースだと思うので、0.0で直接テストすることができます。
 
WHRoeder:

ブローカーからの2倍値は、1.23457500000000から1.2345849999999のどこにあっても、同じ1.23458の価格とみなされるのです。

私は一般的に同意します。 私の上の投稿の P1とP2を参照してください。

WHRoeder です。

しかし、あなたの関数は、1.2345750000000000は1.23458499999999のGToEではないと言っています。

しかし、あなたの関数は、1.23458499999999は、1.23457500000000のLToEではないと言っています。

この問題は、MT4/MQLが浮動小数点値を変数に格納する方法から生じます。 例えば

double p1 = 1.234575000000000000, p2 = 1.23458499999999999;
Print ("p1 = ", DoubleToStr(p1, 8), " p2 = ", DoubleToStr(p2, 8));

は、ログ/ジャーナルに2つの変数を表示します。

ComparePricesテスト1回目

見ての通り、p2は1.2345849999999ではなく、1.23458500になっています。 下のコードにあるように、あなたのコードも 同じように、p1はp2に対してGToEではなく、p1はp2に対してNot Equalです。

double p1 = 1.234575000000000000, p2 = 1.23458499999999999;
Print ("p1 = ", DoubleToStr(p1, 8), " p2 = ", DoubleToStr(p2, 8));
Print ("GToE: ", p1 >= p2);
Print ("ComparePrices() for GToE: ", ComparePrices(p1, p2, GToE));
Print ("WHRoeder GToE: ", p1 - p2 > -Point/2.);
Print ("WHRoeder NEQ: ", MathAbs(p1 - p2) > Point / 2.);

ComparePricesテスト#2

あなたは比較にPoint/2を使用しなければなりません。

Point/2は最大偏差が小さすぎる可能性があります。 例えば

double p1 = 1.234575000000000000, p2 = 1.23458499999999999, p3 = 1.234580;
Print ("p1 = ", DoubleToStr(p1, 8), " p2 = ", DoubleToStr(p2, 8), " p3 = ", DoubleToStr(p3, 8));
Print ("#1 WHRoeder NEQ: ", MathAbs(1.234575000000000000 - 1.23458499999999999) > Point / 2.);
Print ("#2 WHRoeder NEQ: ", MathAbs(p1 - p3) > Point / 2.);
Print ("#3 WHRoeder NEQ: ", MathAbs(p2 - p3) > Point / 2.);

ComparePricesテスト#3

1.234575と1.234580が等しいという前提であれば、なぜ#2はNEQと表示されるのでしょうか? さらに、1.23458 はブローカーからの価格が 1.23457500000000 から 1.2345849999999 までのどこかを意味する価格であると仮定すると、なぜ#1 は NEQ を示すべきなのでしょうか? 同じ価格帯であれば同等であるべきではないでしょうか(したがって、私の上記の投稿の前提条件#2)?

 

13歳。


あなたのコードでは、浮動小数点数エラーによる非意図的な丸め誤差ではなく 、アプリケーションロジックによる意図的な丸め誤差を見て いるため、このような違いが生じます。

丸め」の種類は2つ。

a) IEEEフォーマットの2進数分数による本質的な丸め誤差 。 - これらの数値は全く同じであることを意図していますが、10進数の端数を2進数で表現しているため、そうではありません。これらの数値は、小数のMQ4表現によって丸められます。

b) 小数点以下への明示的な丸め。(例:印刷時、またはブローカーに価格を送信する場合)。- これらは実際には同じ値であることを意味せず、誰かの都合でアプリケーションロジックによって丸められます。

これは本当のエラーではありません。浮動小数点表現にのみ起因するエラーは、(よほどひどい計算をしない限り)ここまで大きく なることはないでしょう。しかし、アプリケーションでこのような比較を独自のロジックで行いたい場合があります。


本質的な丸め誤差[a]は、通常非常に小さく(ポイントより1桁小さい)、意図的なものでは ありません。アプリケーションは、double データ型を 使用して、これらの数値を正確に意図した値に丸めることができません。

明示的な丸めの違い[b]は意図的な もので、より大きな ものです(+/-0.5 ポイント)。(この場合)。したがって、アプリケーション・ロジックによって同じポイント値に丸められた2つの数値は、元々ほぼ1ポイント離れている可能性があります。


理想的には、まず数字を丸め[b](丸めが必要な場合のみ) 、次に それらを比較し[a]、その時点でダブルの 制限により誤差は非常に小さくなります。(例: < 0.0000001)

しかし、あなたのコードは丸める前に 比較するもので、その場合、より大きな差が発生する可能性があることを詳細に説明する必要があります。しかし、四捨五入は常に必要ではありません。私はブローカーに価格を送信するときだけそれを使用します。


もしMQ4が10進数の端数を正確に表現できるBinary Coded Decimalを 採用していたら、Price != Priceに関する問題はすべて解決していたでしょう。

しかし、 特定の操作 のために、アプリケーションの中で数値を最も近いところに丸め、比較する必要があることに変わりはありません。(主にOrderXXX関数)


1.23458を価格とすると、ブローカーからの価格は1.23457500000000から1.23458499999999のどこにでもあることを意味します" >>。

私は間違っているかもしれませんが(ブローカーがどのように機能するか分からない)、ブローカーからの1.23458という価格はまさにその通りだと思います。そうでなければ、公表価格の差を利用して(ブローカーが)大儲けできる。

私の理解では、それは本当にあなたがアプリケーション全体ではなく、ブローカーに送信するときにのみ丸めなければならないということです。その場合、小さな誤差の比較で十分でしょう。

浮動小数点の不正確さは、ブローカー価格の丸めとは別のものです。しかし、もし両方を同時に処理したいのであれば、それは個人の好みだと思います(しかし、混乱するかも?)

 

以下は私の完全版です(バグがないことを祈ります)。

これは6つの機能を提供します。

eq(a,b) =
ne(a,b) !=
gt(a,b) >
lt(a,b) <
ge(a,b) >=
le(a,b) <=

if (ge(Bid,target)) sell sell sell...


その理由は、コードを読みやすくし(IMO)、タイピングエラーの可能性を減らし、パフォーマンスをあまり上げないようにするためです。

これらの関数は、MQ4のユーザー関数と同じように高速に動作するはずです。

(パフォーマンスについては、MathAbs(a-b) < HalfPoint を参照してください。https://www.mql5.com/en/forum/136997/page5#822505 しかし、実際のEAでは(ベンチマークとは対照的に)その差は重要ではないと思われます。


bool gt(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a < b) {
      return (false);
   } else {
      return (a-b > p2);
   }
}
bool lt(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a > b) {
      return (false);
   } else {
      return (b-a > p2);
   }
}
bool ge(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a >= b) {
      return (true);
   } else {
      return (b-a <= p2);
   }
}
bool le(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a <= b) {
      return (true);
   } else {
      return (a-b <= p2);
   }
}
bool eq(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a > b) {
      return (a-b <= p2);
   } else {
      return (b-a <= p2);
   }
}

bool ne(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a > b) {
      return ((a-b) > p2);
   } else {
      return ((b-a) > p2);
   }
}
 
ydrol:

以下は私の完全版です(バグがないことを祈ります)...

...

bool eq(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a > b) {
      return (a-b <= p2);
   } else {
      return (b-a <= p2);
   }
}

よく言われる前提は

  • ブローカーからの2倍値は、1.23457500000000から1.23458499999999のどこにあっても、同じ1.23458の価格と見なされる。

その前提で、あなたのコードを背景にして考えると、(a) 1.234576 と 1.234584 は等しくないと 考えられ、(b) 1.234577 と 1.234583 は等しくないと 考えられ、しかし (c) 1.234578 と 1.234582は等しいと 考えられる理由を説明してもらえますか? なぜ(どのように)(b)は(c)よりも等しくないのでしょうか?

先に 述べたように、私はこれらの価格はすべて同じ価格帯、つまり1.23458を共有しているため、同等であると考えます。 この例は、私がポイント/2は最大偏差が小さすぎると考えている(と上記で述べて いる)理由を示しています。

 

@Thirteen, あなたの観察に対する私の答えは、3つ上の記事と同じままです https://www.mql5.com/en/forum/136997/page5#822672link.私の視点を理解する上で、光明となりそうな部分を繰り返します。(少し修正し、強調を加えています)

Think of it another way (If MQ4 had used Binary Coded Decimal - which allows exact representation of Decimal fractions - then most of the original issues regarding Price != Price would go away, (and is often used on financial platforms for that very reason )

しかし、 特定の操作の ために、アプリケーション内の数値を最も近い点に丸め、比較しなければならないことに変わりはありません。(主に OrderXXX 関数)


アプリケーションの丸め(単純化/利便性のために2つの異なる数値を概念的/論理的に同じものとして扱うこと)を区別するかどうかは、コードをどのように書くかによります。

と浮動小数点演算の誤差を区別するかどうかです。どちらが正しいということはありませんが、どちらかのアプローチの方が分かりやすいと思います...。


さらに、個人的には、先の投稿で再び言及された、引用外の前提(ただし訂正は受け付けます!)には少し懐疑的なのです。