Implementações alternativas de funções/abordagens padrão - página 3

 
fxsaber:
Acontece que a #import ex5 é o mal da otimização.

Em termos de ser capaz de otimizar globalmente, sim.

Temos um esquema bastante agressivo sem tentar fazer o código menor. Assim, no modo de otimização global, geramos um código muito bom.

Isto pode ser visto no tempo de compilação, onde colocamos a velocidade resultante no topo da lista.

 
prostotrader:

fxsaber

Há um erro em seu código

Obrigado, eu corrigi isso.
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:
Obrigado, corrigido.

Ainda é um erro.

Deve ser um 5 no final.

 
prostotrader:

Ainda um erro

Não é um erro, é um arredondamento. Isto é exatamente o que a versão padrão faz.
 
fxsaber:
Isto não é um erro, é um arredondamento. É assim que a versão padrão o faz.
Eu cometi um erro com a tela errada, olhe novamente (substituir imagem)
 

Aqui está o código para você testar

//+------------------------------------------------------------------+
//|                                                         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:

Aqui está o seu código, teste-o.

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 - correto. Val3 após conversão para longo - não correto. Aparentemente, é alguma peculiaridade da dupla representação dos números de ponto flutuante. Precisamos incrementar o EPSILON. Eu não consigo me virar de cabeça adormecida. Talvez algumas pessoas conhecedoras possam me dar uma dica.

Preciso descobrir que considerações os desenvolvedores usaram para escrever isto

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

Parece ser aqui que o cão está enterrado.

 
Agora funciona sempre corretamente, mas apenas 10% mais rápido do que o original.
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, Obrigado por encontrar as discrepâncias!
 
fxsaber:

Parece ser aqui que o cão está enterrado.

As raízes crescem a partir do fórum RSDN

DBL_EPSILON define uma diferença de 1 (um!) bit significativo de um expoente quando aplicado ao número 1.0. Na prática, não há tal diferença - os números são estritamente iguais ou podem diferir em mais de um bit significativo. Portanto, é preciso tomar algo como 16*DBL_EPSILON para ignorar a diferença de 4 bits menos significativos (ou cerca de um dígito decimal e meio último significativo de cerca de 16 disponíveis).

Naturalmente, há casos em que a gama de números é mais ou menos conhecida e previsível. Digamos, 0...1000. Neste caso, você pode tomar uma constante como 1000*16*DBL_EPSILON para uma comparação aproximada. Mas devemos ter em mente que tal comparação realmente transforma toda a idéia do ponto flutuante em um ponto fixo (adivinhe por quê).

 

Uma variante do CopyTicks, que às vezes é várias ordens de magnitude mais rápida do que a original (de > 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);
}