エラー、バグ、質問 - ページ 2822

 
Nikolai Semko:

標準の round(), ceil(), floor() も double を返すので、丸めだけは行われない。

しかし、これらを通じて、特に彼らは通常のものよりも速く動作します。

その方が速いかもしれないが、それは間違っている。
12345.0000000000001のようなものをceilに渡すと(あなたの例と同様)、出力に12346を得ることができます。
 
Alexey Navoykov:
その方が速いかもしれないが、それは間違っている。
12345.0000000000001のようなもの(あなたの例と同様)をceilに渡すと、出力に12346を得ることができます。

ご自身で試されたことはありますか?
試してみてください。

Print(ceil( 12345.0000000000001));
Print(Ceil( 12345.0000000000001));
Print(ceil( 12345.000000000001));
Print(Ceil( 12345.000000000001));

出力します。

2020.08.10 12:03:23.856 ErrorNormalizeDouble (EURUSD,M1)        12345.0
2020.08.10 12:03:23.856 ErrorNormalizeDouble (EURUSD,M1)        12345
2020.08.10 12:03:23.856 ErrorNormalizeDouble (EURUSD,M1)        12346.0
2020.08.10 12:03:23.856 ErrorNormalizeDouble (EURUSD,M1)        12346
は、ceil(「上から最も近い整数の数値を返す」)なので、12346になるはずです。
最初のケースは12345です。ダブル型の有効桁数は17ですが、18を持っているからです。
 
Nikolai Semko:

本当に、ダブルは比べものになりませんね。ただ、難しいルールなんです。

もちろん、ダブルス同士を直接比較することは可能であり、時には必要な場合もあります。

例えば、OnTickはOptimization中に1兆回呼ばれることもあります。保留中の指値を実行するかどうかを理解するために、内蔵のテスターは現在の対応するシンボル価格と指値価格を比較します。各オンティックコールの前に、各保留注文に対して行われます。つまり、これらのチェックは何百億回、何千億回と行われているのです。

そして、それは正規化によってその都度行われます。まあ、これは計算機資源の 無駄遣いとしか言いようがない。保留中の注文とシンボルの価格はあらかじめ正規化されているので。そのため、両者を直接比較することができますし、そうする必要があります。

MQL-customのMQL Testerは、ネイティブのビルトインテスターを性能面で容易に凌駕しています。

 

fxsaber
:

もちろん、ダブルス同士を直接比較することは可能であり、時には必要な場合もあります。

例えば、OnTickはOptimizeの時に1兆回呼ばれることがあります。内蔵のテスターは、保留中の指値を実行するかどうかを理解するために、現在の対応するシンボル価格と指値価格を比較します。各オンティックコールの前に、各保留注文に対して行われます。つまり、これらのチェックは何百億回、何千億回と行われているのです。

そして、それは正規化によってその都度行われます。まあ、これは計算機資源の 無駄遣いとしか言いようがない。保留中の注文とシンボルの価格はあらかじめ正規化されているので。そのため、両者を直接比較することができますし、そうする必要があります。

MQL-customのMQL Testerは、ネイティブのビルトインテスターを性能面で容易に凌駕しています。

NormalizeDouble()は非常に高価な関数です。ですから、忘れた方がいいんです。

NormalizeDouble()とintで正規化することの違いを示すスクリプトを紹介します。

#define   SIZE 1000000

int Ceil (double x) {return (x-(int)x>0)?(int)x+1:(int)x;}
int Round(double x) {return (x>0)?(int)(x+0.5):(int)(x-0.5);}
int Floor(double x) {return (x>0)?(int)x:((int)x-x>0)?(int)x-1:(int)x;}
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   double a[SIZE];
   double s1=0,s2=0, s3=0;
   for (int i=0;i<SIZE;i++)  a[i]=(rand()-16384)/M_PI;
   
   ulong t1=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s1+=a[i];
   t1=GetMicrosecondCount()-t1;  
   
   ulong t2=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s2+=NormalizeDouble(a[i],5);
   t2=GetMicrosecondCount()-t2; 
   
   ulong t3=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s3+=Round(a[i]*100000);
   s3/=100000;
   t3=GetMicrosecondCount()-t3; 
   
   Print("простая сумма                            - " + string(t1)+ " микросекунд, сумма = "+ DoubleToString(s1,18));
   Print("сумма с NormalizeDouble                  - " + string(t2)+ " микросекунд, сумма = "+ DoubleToString(s2,18));
   Print("сумма, нормализированная через int       - " + string(t3)+ " микросекунд, сумма = "+ DoubleToString(s3,18));
  }

の結果です。

2020.08.10 12:55:30.766 TestSpeedNormalizeDouble (USDCAD,H4)    простая сумма                            - 1394 микросекунд, сумма = 626010.5038610587362201
2020.08.10 12:55:30.766 TestSpeedNormalizeDouble (USDCAD,H4)    сумма с NormalizeDouble                  - 5363 микросекунд, сумма = 626010.5046099 795727060
2020.08.10 12:55:30.766 TestSpeedNormalizeDouble (USDCAD,H4)    сумма, нормализированная через int       - 1733 микросекунд, сумма = 626010.5046099999 453873
SZZでは、intによる正規化もより正確です(正規化の最後の桁の後の9の数でわかります-青でハイライトされています)。
 
Nikolai Semko:

NormalizeDouble()は非常に高価な関数です。だから、忘れたほうがいいんです。

以下は、NormalizeDouble()とintによる正規化との違いを示すスクリプトです。

の結果です。

SZZでは、intによる正規化がより正確です(正規化の最後の桁の後の9の数でわかります-青でハイライトされています)。

また、合計が double ではなく long である場合、int での合計(乗算と丸め、そして最終的な合計を割る)は通常の double の合計よりも高速に計算されるので、結果はさらに素晴らしいものになります。

#define   SIZE 1000000

int Ceil (double x) {return (x-(int)x>0)?(int)x+1:(int)x;}
int Round(double x) {return (x>0)?(int)(x+0.5):(int)(x-0.5);}
int Floor(double x) {return (x>0)?(int)x:((int)x-x>0)?(int)x-1:(int)x;}
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   double a[SIZE];
   double s1=0,s2=0, s3=0;
   long s=0;
   for (int i=0;i<SIZE;i++)  a[i]=(rand()-16384)/M_PI;
   
   ulong t1=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s1+=a[i];
   t1=GetMicrosecondCount()-t1;  
   
   ulong t2=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s2+=NormalizeDouble(a[i],5);
   t2=GetMicrosecondCount()-t2; 
   
   ulong t3=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s+=Round(a[i]*100000);
   s3=s/100000.0;
   t3=GetMicrosecondCount()-t3; 
   
   Print("простая сумма                            - " + string(t1)+ " микросекунд, сумма = "+ DoubleToString(s1,18));
   Print("сумма с NormalizeDouble                  - " + string(t2)+ " микросекунд, сумма = "+ DoubleToString(s2,18));
   Print("сумма, нормализированная через int       - " + string(t3)+ " микросекунд, сумма = "+ DoubleToString(s3,18));  
  }

の結果です。

2020.08.10 13:15:58.982 TestSpeedNormalizeDouble (USDCAD,H4)    простая сумма                            - 1408 микросекунд, сумма = 460384.3207830497995019
2020.08.10 13:15:58.982 TestSpeedNormalizeDouble (USDCAD,H4)    сумма с NormalizeDouble                  - 6277 микросекунд, сумма = 460384.3162300114054233
2020.08.10 13:15:58.982 TestSpeedNormalizeDouble (USDCAD,H4)    сумма, нормализированная через int       - 964 микросекунд,  сумма = 460384.3162299999967218
 
Nikolai Semko:

また、合計がdouble経由ではなくlong経由の場合は、int経由の合計(乗算と丸め、その後に合計の除算)の方が通常のdoubleの合計より速いので、結果はさらに素晴らしいものになります。

の結果です。

比較のための10進数 加算。

リンクが違います、完全な実装ではありません。

 
fxsaber:

そして、それは毎回ノーマライゼーションによって行われます。まあ、これはコンピューティングリソースの 無駄遣いとしか言いようがない。

なぜわかるかというと、価格が正規化されていなくても、チェックは単純に行われるからです。

 if (fabs(price-limitprice) < ticksize/2)

価格がティックサイズの倍数であることを考えると

 
Nikolai Semko:
さらに、intによる正規化もより正確であることがわかります(正規化の最後の桁の後の9の数でわかります-青でハイライトされています)。

テストが正しくありません。 なぜ最後に一度だけ100000. 0で割るのですか? 反復ごとに実行し、合計すべきです。 これは公平な比較です。 しかし、これは全く正規化ではありません。あなたはテストアルゴリズムを最適化しただけ です。当然ながら、より速く、より正確に(累積誤差が減るので)なります。

 
Alexey Navoykov:

どうしてわかるんですか?

なぜなら、正規化されていない価格をテスターに入力しても、同じように処理されるからです。

結局のところ、価格が正規化されていなくても、チェックは簡単にできるのです。

正規化とは、この場合、単一の標準アルゴリズムを適用した後、この標準の2倍を直接比較できるようにすることを意味します。

だから、テスターは直接ダブルを比較するわけではありません。NormalizeDoubleや ticksizeなどで行っています。しかし、確かに2倍速の直接比較ではありません。そして、それはまったく合理的ではありません。

 
fxsaber:

もちろん、ダブルス同士を直接比較することは可能であり、時には必要な場合もあります。

例えば、Optimize OnTickは1兆回呼ばれることもあります。内蔵のテスターは、保留中のリミットコールを実行するかどうかを理解するために、現在の対応するシンボル価格とリミットコール価格を比較します。各オンティックコールの前に、各保留注文に対して行われます。つまり、これらのチェックは何百億回、何千億回と行われているのです。

そして、それは正規化によってその都度行われます。まあ、これは計算機資源の 無駄遣いとしか言いようがない。保留中の注文とシンボルの価格はあらかじめ正規化されているので。そのため、両者を直接比較することができますし、そうする必要があります。

MQL-customのMQL Testerは、ネイティブのビルトインテスターに性能で勝ることはありません。

そこで、性能ナンセンス版を確認することにしました。

正規化前の double の比較でも、εやintへの変換で double を比較した場合よりも、平均してさらに遅くなります。

#define  SIZE 1000000

int Ceil (double x) {return (x-(int)x>0)?(int)x+1:(int)x;}
int Round(double x) {return (x>0)?(int)(x+0.5):(int)(x-0.5);}
int Floor(double x) {return (x>0)?(int)x:((int)x-x>0)?(int)x-1:(int)x;}

bool is_equal(double d1, double d2, double e=0.000000001) {return fabs(d1-d2)<e;}

void OnStart()
  {
   double a[SIZE], a_norm[SIZE];
   int s1=0,s2=0, s3=0;
   for (int i=0;i<SIZE;i++)  {
     a[i]=(rand()-16384)/1641.1452;
     a_norm[i]=NormalizeDouble(a[i],2);
   }
   double test = 1.11;
   
   ulong t1=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) if (a_norm[i]==test) s1++;
   t1=GetMicrosecondCount()-t1;  
   
   ulong t2=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) if (is_equal(a[i],test,0.005)) s2++;
   t2=GetMicrosecondCount()-t2; 
   
   ulong t3=GetMicrosecondCount();
   int test_int = test*100;
   for (int i=0;i<SIZE;i++) if (Round(a[i]*100)==test_int) s3++;
   t3=GetMicrosecondCount()-t3; 
   
   
   Print("простое сравнение предварительно нормализированых double - " + string(t1)+ " микросекунд, всего совпадений = "+ string(s1));
   Print("сравнение double через эпсилон                           - " + string(t2)+ " микросекунд, всего совпадений = "+ string(s2));
   Print("сравнение double через преобразование в int              - " + string(t3)+ " микросекунд, всего совпадений = "+ string(s3));  
  }

その結果

2020.08.10 14:31:39.620 TestCompareDouble (USDCAD,H4)   простое сравнение предварительно нормализированых double - 900  микросекунд, всего совпадений = 486
2020.08.10 14:31:39.620 TestCompareDouble (USDCAD,H4)   сравнение double через эпсилон                           - 723  микросекунд, всего совпадений = 486
2020.08.10 14:31:39.620 TestCompareDouble (USDCAD,H4)   сравнение double через преобразование в int              - 805  микросекунд, всего совпадений = 486
2020.08.10 14:31:42.607 TestCompareDouble (USDCAD,H4)   простое сравнение предварительно нормализированых double - 1533 микросекунд, всего совпадений = 488
2020.08.10 14:31:42.607 TestCompareDouble (USDCAD,H4)   сравнение double через эпсилон                           - 758  микросекунд, всего совпадений = 488
2020.08.10 14:31:42.607 TestCompareDouble (USDCAD,H4)   сравнение double через преобразование в int              - 790  микросекунд, всего совпадений = 488
2020.08.10 14:31:44.638 TestCompareDouble (USDCAD,H4)   простое сравнение предварительно нормализированых double - 986  микросекунд, всего совпадений = 472
2020.08.10 14:31:44.638 TestCompareDouble (USDCAD,H4)   сравнение double через эпсилон                           - 722  микросекунд, всего совпадений = 472
2020.08.10 14:31:44.638 TestCompareDouble (USDCAD,H4)   сравнение double через преобразование в int              - 834  микросекунд, всего совпадений = 472

プロセッサの新規性やアーキテクチャに大きく依存することは否定できませんし、人によって結果は異なるかもしれません。


どうやら、コンパイラが乱数の和を最適化する機能がないようです。丸めを括弧の外に出してはいけない。
プロセッサの2重比較は1コマンドのようです
イプシロンで比較する場合(最も高速な方法)、2つの2重の比較操作があるのは変わりませんが、それに加えて、3つのパラメータを渡す関数呼び出しと1つの引き算の操作をしているのです。
2つのダブルの比較という操作の性能は、変数の値そのものに依存するのでしょうか?そうでしょうか。
げっ、意味わかんない。私は何を考慮に入れなかったのか、どこで間違ったのか、助けてください。