標準的な機能/アプローチの代替実装 - ページ 3

 
fxsaber:
import ex5が最適化の弊害であることが判明。

グローバルに最適化できるという点では、そうですね。

コードを小さくしようとせず、かなり積極的にインライン化しています。そのため、全体最適化モードでは、非常に優れたコードを生成することができます。

これは、コンパイル時に、結果のスピードを最優先していることからもわかります。

 
prostotrader:

ファクスセーバー

あなたのコードにエラーがあります

ありがとうございます、修正しました。
double MyNormalizeDouble( const double Value, const uint digits )
{
  static const double Points[] = {1.0 e-0, 1.0 e-1, 1.0 e-2, 1.0 e-3, 1.0 e-4, 1.0 e-5, 1.0 e-6, 1.0 e-7, 1.0 e-8};
  const double point = digits > 8 ? 1.0 e-8 : Points[digits];

  return((long)((Value > 0) ? Value / point + HALF_PLUS : Value / point - HALF_PLUS) * point);
}
 
fxsaber:
ありがとうございます、訂正しました。

それでも間違い。

最後に5になるはずです。

 
prostotrader:

それでもエラー

誤差ではなく、四捨五入です。これはまさに、標準版で行われていることです。
 
fxsaber:
これはエラーではなく、四捨五入です。標準版ではそうなっています。
画面を間違えてしまったので、もう一度見てください(画像を差し替え)。
 

以下は、テスト用のコードです。

//+------------------------------------------------------------------+
//|                                                         Test.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#define  EPSILON (1.0 e-7 + 1.0 e-13)
#define  HALF_PLUS  (0.5 + EPSILON)
#property indicator_separate_window
#property indicator_plots   1
#property indicator_buffers 1
double d_value = 12345.012345;
//
double MyNormalizeDouble( const double Value, const uint digits )
{
  static const double Points[] = {1.0 e-0, 1.0 e-1, 1.0 e-2, 1.0 e-3, 1.0 e-4, 1.0 e-5, 1.0 e-6, 1.0 e-7, 1.0 e-8};
  const double point = digits > 8 ? 1.0 e-8 : Points[digits];

  return((long)((Value > 0) ? Value / point + HALF_PLUS : Value / point - HALF_PLUS) * point);
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   double new_value = MyNormalizeDouble(d_value, 0);
   new_value = NormalizeDouble(d_value, 0);
   new_value = MyNormalizeDouble(d_value, 1);
   new_value = NormalizeDouble(d_value, 1);
   new_value = MyNormalizeDouble(d_value, 2);
   new_value = NormalizeDouble(d_value, 2);
   new_value = MyNormalizeDouble(d_value, 3);
   new_value = NormalizeDouble(d_value, 3);
   new_value = MyNormalizeDouble(d_value, 4);
   new_value = NormalizeDouble(d_value, 4);
   new_value = MyNormalizeDouble(d_value, 5);
   new_value = NormalizeDouble(d_value, 5);
   new_value = MyNormalizeDouble(d_value, 6);
   new_value = NormalizeDouble(d_value, 6);
   new_value = MyNormalizeDouble(d_value, 7);
   new_value = NormalizeDouble(d_value, 7);
   new_value = MyNormalizeDouble(d_value, 8);
   new_value = NormalizeDouble(d_value, 8);
   new_value = MyNormalizeDouble(d_value, 9);
   new_value = NormalizeDouble(d_value, 9);
   if (new_value ==0.0)
   {}  
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
 
prostotrader:

これがあなたのコードです、テストしてみてください。

void OnStart( void )
{
  double Val = 12345.012345;
  
  double Val2 = Val / 1.0 e-5;
  Val2 += HALF_PLUS; // Val2 == 1234501235.0
  
  long Val3 = (long)Val2; // Val3 == 1234501234
    
  return;
};

Val2 - 正解です。Longに変換後のVal3 - 正しくありません。どうやら、浮動小数点数の二重表現の特殊性のようです。EPSILONをインクリメントする必要があります。寝ぼけた頭で考えてもわからない。もしかしたら、知識のある方がヒントをくれるかもしれません。

開発者が どのような配慮でこれを書いたのか把握する必要がある

//+------------------------------------------------------------------+
//| Сравнивает два значения типа double.                             |
//| RESULT                                                           |
//|   Возвращает истину, если значения равны и                       |
//|   ложь в противном случе.                                        |
//+------------------------------------------------------------------+
bool CEnvironment::DoubleEquals(const double a,const double b)
  {
//---
   return(fabs(a-b)<=16*DBL_EPSILON*fmax(fabs(a),fabs(b)));
//---
  }

ここが犬の埋葬場所のようです。

 
これで常に正常に動作するようになりましたが、10%しか速くなりません
double MyNormalizeDouble( const double Value, const uint digits )
{
  static const double Points[] = {1.0 e-0, 1.0 e-1, 1.0 e-2, 1.0 e-3, 1.0 e-4, 1.0 e-5, 1.0 e-6, 1.0 e-7, 1.0 e-8};
  const double point = digits > 8 ? 1.0 e-8 : Points[digits];
  const long Integer = (long)Value; // чтобы не создавать крайне медленный относительный epsilon

  return((long)((Value > 0) ? (Value - Integer) / point + HALF_PLUS : (Value - Integer) / point - HALF_PLUS) * point + Integer);
}
prostotrader さん、矛盾点を発見していただきありがとうございます
 
fxsaber:

ここが犬の埋葬場所のようです。

RSDNフォーラムから根を生やす

DBL_EPSILON は、数値 1.0 に適用した場合の指数の有効ビット 1 (one!) の差を定義しています。実際には、このような差はなく、数値は厳密に等しいか、あるいは有効ビット数以上の差があることがあります。したがって、16*DBL_EPSILONの ように、最下位4ビット(または約16桁あるうちの最下位1.5桁)の差を無視するような値を取る必要があります。

もちろん、数字の範囲が多少なりともわかっていて、予測できる場合もある。例えば、0...1000とします。この場合、1000*16*DBL_EPSILONのような定数を取ると、大まかな比較ができます。しかし、このような比較は、実際には浮動小数点の考え方を固定小数点に変えてしまうことを念頭に置かなければなりません(理由を推測してください)。

 

CopyTicks の亜種で、オリジナルより数桁速いこともある (from > 0)

int MyCopyTicks( const string Symb, MqlTick& Ticks[], const uint flags = COPY_TICKS_ALL, const ulong from = 0, const uint count = 0 )
{
  int Res = (from == 0) ? CopyTicks(Symb, Ticks, flags, 0, count) : -1;
  
  if (from > 0)
  {    
    uint count2 = 1;
    
    MqlTick NewTicks[];    
    int Amount = CopyTicks(Symb, NewTicks, flags, 0, count2);
    
    while ((Amount > 0) && ((ulong)NewTicks[0].time_msc >= from))
    {
      count2 <<= 1;
      
      Amount = CopyTicks(Symb, NewTicks, flags, 0, count2);
    }

    for (int i = 1; i < Amount; i++)
      if ((ulong)NewTicks[i].time_msc >= from)
      {
        Res = ArrayCopy(Ticks, NewTicks, 0, i, (count == 0) ? 2000 : count);
        
        break;
      }    
  }
  
  return(Res);
}
理由: