Альтернативные реализации стандартных функций/подходов - страница 3

 
fxsaber:
Получается, что #import ex5 - это зло оптимизации.

С точки зрения возможности глобальной оптимизации - да.

У нас достаточно агрессивный инлайнинг без попыток сделать код меньше. Поэтому в режиме глобальной оптимизации мы генерируем очень хороший код.

Это видно по времени компиляции, где мы ставим во главу угла именно результирующую скорость.

 
prostotrader:

fxsaber

В Вашем коде ошибка

Спасибо, исправил.
double MyNormalizeDouble( const double Value, const uint digits )
{
  static const double Points[] = {1.0e-0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8};
  const double point = digits > 8 ? 1.0e-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.0e-7 + 1.0e-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.0e-0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8};
  const double point = digits > 8 ? 1.0e-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.0e-5;
  Val2 += HALF_PLUS; // Val2 == 1234501235.0
  
  long Val3 = (long)Val2; // Val3 == 1234501234
    
  return;
};

Val2 - правильно. Val3 после приведения к long - не правильно. Видимо, какая-то особенность double-представления чисел с плавающей точкой. Надо увеличивать 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.0e-0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8};
  const double point = digits > 8 ? 1.0e-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 (один!) значащий бит экспоненты в приложении к числу 1.0. На практике такой разницы не встречается — числа либо строго равны, либо могут различаться больше чем на один значащий бит. Поэтому надо брать что-то типа 16*DBL_EPSILON, чтобы игнрорировать разницу в 4 младших бита (или примерно полторы последние значащие десятичные цифры из примерно 16 имеющихся). 

Конечно же, есть случаи, когда диапазон чисел более-менее известен и предсказуем. Скажем, 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);
}