Erros, bugs, perguntas - página 2822

 
Nikolai Semko:

Apenas o arredondamento não é feito utilizando a ronda(), ceil(), chão() padrão, porque também devolvem o dobro.

Mas através destes, especialmente trabalham mais rapidamente do que os normais:

Pode ser mais rápido, mas é simplesmente errado.
Passe algo como 12345.0000000000001 para o seu tejadilho (semelhante ao seu exemplo), e pode obter 12346 na saída
 
Alexey Navoykov:
Pode ser mais rápido, mas é simplesmente errado.
Passe algo como 12345.0000000000001 (semelhante ao seu exemplo) para o seu telhado, e pode obter 12346 na saída.

Já o experimentou?
Experimente-o:

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

Produção:

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
deve ser 12346, porque é um tejadilho ("Retorna o valor numérico inteiro mais próximo de cima")
o primeiro caso é 12345, porque os dígitos significativos em tipo duplo são 17, enquanto que tem 18
 
Nikolai Semko:

Realmente, não se podem comparar as duplas. É apenas uma regra difícil.

Naturalmente, é possível e por vezes até necessário comparar directamente entre si as duplas.

Por exemplo, o OnTick é por vezes chamado um trilião de vezes durante a Optimização. A fim de compreender se deve ou não executar um limite pendente, o testador incorporado compara o preço actual do símbolo correspondente e o preço limite. Fá-lo para cada encomenda pendente antes de cada chamada OnTick. Ou seja, estes controlos são feitos dezenas e centenas de milhares de milhões de vezes.

E é feito de cada vez através da normalização. Bem, isto é um desperdício horrível de recursos informáticos. Uma vez que os preços das encomendas pendentes e o símbolo são preliminarmente normalizados. Portanto, podem e devem ser comparados directamente uns com os outros.

O MQL-tester de MQL-personalizado tem um desempenho facilmente superior ao do testador nativo integrado em desempenho.

 

fxsaber
:

Naturalmente, é possível e por vezes até necessário comparar directamente entre si as duplas.

Por exemplo, OnTick é por vezes chamado um trilião de vezes durante a Optimize. O testador incorporado, a fim de compreender se deve ou não executar um limite pendente, compara o preço actual do símbolo correspondente e o preço limite. Fá-lo para cada encomenda pendente antes de cada chamada OnTick. Ou seja, estes controlos são feitos dezenas e centenas de milhares de milhões de vezes.

E é feito de cada vez através da normalização. Bem, isto é um desperdício horrível de recursos informáticos. Uma vez que os preços das encomendas pendentes e o símbolo são preliminarmente normalizados. Portanto, podem e devem ser comparados directamente uns com os outros.

O MQL-tester de MQL-personalizado tem um desempenho facilmente superior ao do testador nativo integrado em desempenho.

NormalizeDouble() é uma função muito cara. Por conseguinte, é melhor esquecer isso.

Aqui está um guião que demonstra a diferença entre NormalizeDouble() e normalize com 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));
  }

resultado:

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 a normalização por int é também mais precisa (pode vê-la pelo número de noves após o último dígito da normalização - realçado a azul).
 
Nikolai Semko:

NormalizeDouble() é uma função muito cara. É por isso que é melhor esquecê-lo.

Aqui está um guião que demonstra a diferença entre NormalizeDouble() e normalize com int:

resultado:

SZZ a normalização por int é ainda mais precisa (pode ver-se isto pelo número de noves após o último dígito da normalização - realçado a azul).

e se a soma não é via dupla, mas via longa, então o resultado é ainda mais impressionante, visto que a soma via int (multiplicação e arredondamento seguido pela divisão da soma final) calcula mais rapidamente do que a soma normal da dupla.

#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));  
  }

resultado:

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:

E se a soma não é via dupla, mas via longa, então o resultado é ainda mais impressionante, porque a soma via int (multiplicação e arredondamento, seguido pela divisão da soma total) é mais rápida do que uma soma dupla normal.

resultado:

Decimal para comparação adicionar.

Referência errada, não é uma implementação completa.

 
fxsaber:

E é feito através da normalização de cada vez. Bem, isto é um terrível desperdício de recursos informáticos.

Como é que sabe? Porque mesmo que os preços não sejam normalizados, a verificação é simplesmente feita sem qualquer normalização:

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

Dado que os preços são múltiplos de carraças de tamanho

 
Nikolai Semko:
Além disso, a normalização através de int também se revela mais precisa (pode vê-la pelo número de noves após o último dígito de normalização - realçado a azul).

O teste está incorrecto. Porque se divide por 100000.0 apenas uma vez no final? Deve ser realizado em cada iteração e depois resumido. É uma comparação justa. Mas isto não é normalização de todo - acabou de optimizar o seu algoritmo de teste. Naturalmente, será mais rápido e mais preciso (porque o erro acumulado é reduzido).

 
Alexey Navoykov:

Como é que sabe isto?

Porque pode introduzir preços não-normalizados ao Testador e este trata-os de forma idêntica.

Afinal, mesmo que os preços não estejam normalizados, a verificação é facilmente feita sem qualquer normalização.

Por normalização quis dizer neste caso, um único algoritmo padrão, após a sua aplicação, é possível comparar directamente duplicações deste padrão.

Assim, o testador não compara directamente as duplas. Fá-lo através de NormalizeDouble, ticksize ou qualquer outra coisa. Mas certamente não através da comparação directa de duplas. E não é de todo racional.

 
fxsaber:

Naturalmente, é possível e por vezes mesmo necessário comparar directamente entre si as duplas.

Por exemplo, o OnTick é por vezes chamado um trilião de vezes durante a Optimize. O testador incorporado, a fim de compreender se deve ou não executar uma chamada de limite pendente, compara o preço actual do símbolo correspondente e o preço da chamada de limite. Fá-lo para cada encomenda pendente antes de cada chamada OnTick. Ou seja, estes controlos são feitos dezenas e centenas de milhares de milhões de vezes.

E é feito de cada vez através da normalização. Bem, isto é um desperdício horrível de recursos informáticos. Uma vez que os preços das encomendas pendentes e o símbolo são preliminarmente normalizados. Portanto, podem e devem ser comparados directamente uns com os outros.

O MQL-tester de MQL-personalizado não bate o testador nativo incorporado em desempenho.

Por isso, decidi verificar a versão sem sentido do desempenho.
E o resultado foi surpreendente.
A comparação até do dobro pré-normalizado é ainda mais lenta em média do que quando se compara o dobro através do epsilon ou conversão para 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;}

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));  
  }

O resultado:

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

Não excluo que muito dependa da novidade e arquitectura do processador, e para alguém o resultado pode ser diferente.

Para dizer a verdade - nem sequer compreendo porque acontece.
Parece que o compilador não tem nada a optimizar com a soma de números aleatórios. Não se pode colocar arredondamento de parênteses.
Parece que a comparação dupla no processador é um comando
Ao comparar através do epsilon (a forma mais rápida) ainda temos uma operação de comparação de dois duplos, mas além disso temos uma chamada de função com passagem de três parâmetros e uma operação de subtracção.
O desempenho da operação de comparação de duas duplas depende dos valores das próprias variáveis? Duvido.
Caramba, não percebo. Por favor, ajude-me, o que é que eu não tive em conta ou onde é que errei?

Razão: