Errori, bug, domande - pagina 2822

 
Nikolai Semko:

Solo l'arrotondamento non viene fatto usando gli standard round(), ceil(), floor() perché anch'essi restituiscono doppio.

Ma attraverso questi, soprattutto lavorano più velocemente di quelli regolari:

Può essere più veloce, ma è semplicemente sbagliato.
Passate qualcosa come 12345.0000000000001 nel vostro ceil (simile al vostro esempio), e potete ottenere 12346 nell'output
 
Alexey Navoykov:
Potrebbe essere più veloce, ma è semplicemente sbagliato.
Passate qualcosa come 12345.0000000000001 (simile al vostro esempio) nel vostro ceil, e potrete ottenere 12346 nell'output.

L'hai provato tu stesso?
Provate:

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

Uscita:

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
dovrebbe essere 12346, perché è un ceil ("Restituisce il valore numerico intero più vicino dall'alto")
il primo caso è 12345, perché le cifre significative nel tipo doppio sono 17, mentre voi avete 18
 
Nikolai Semko:

Davvero, non si possono paragonare i doppi. È solo una regola dura.

Naturalmente, è possibile e a volte anche necessario confrontare i doppi direttamente tra loro.

Per esempio, OnTick a volte viene chiamato un trilione di volte durante l'ottimizzazione. Per capire se eseguire o meno un limite pendente, il tester integrato confronta il prezzo corrente del simbolo corrispondente e il prezzo limite. Lo fa per ogni ordine pendente prima di ogni chiamata OnTick. Cioè questi controlli vengono fatti decine e centinaia di miliardi di volte.

E lo si fa ogni volta attraverso la normalizzazione. Beh, questo è un orribile spreco di risorse informatiche. Poiché i prezzi degli ordini pendenti e del simbolo sono preliminarmente normalizzati. Pertanto, possono e devono essere confrontati direttamente tra loro.

Il MQL-custom MQL Tester supera facilmente il tester integrato nativo in termini di prestazioni.

 

fxsaber
:

Naturalmente, è possibile e a volte anche necessario confrontare i doppi direttamente tra loro.

Per esempio, OnTick è chiamato un trilione di volte durante Optimize. Il tester integrato, per capire se eseguire o meno un limite pendente, confronta il prezzo corrente del simbolo corrispondente e il prezzo limite. Lo fa per ogni ordine pendente prima di ogni chiamata OnTick. Cioè questi controlli vengono fatti decine e centinaia di miliardi di volte.

E lo si fa ogni volta attraverso la normalizzazione. Beh, questo è un orribile spreco di risorse informatiche. Poiché i prezzi degli ordini pendenti e del simbolo sono preliminarmente normalizzati. Pertanto, possono e devono essere confrontati direttamente tra loro.

Il MQL-custom MQL Tester supera facilmente il tester integrato nativo in termini di prestazioni.

NormalizeDouble() è una funzione molto costosa. Pertanto, è meglio dimenticarsene.

Ecco uno script che dimostra la differenza tra NormalizeDouble() e normalize with 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));
  }

risultato:

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 la normalizzazione per int è anche più accurata (si può vedere dal numero di nove dopo l'ultima cifra della normalizzazione - evidenziata in blu).
 
Nikolai Semko:

NormalizeDouble() è una funzione molto costosa. Ecco perché è meglio dimenticarsene.

Ecco uno script che dimostra la differenza tra NormalizeDouble() e normalize with int:

risultato:

SZZ la normalizzazione per int è ancora più accurata (si può vedere dal numero di nove dopo l'ultima cifra della normalizzazione - evidenziata in blu).

e se la somma non è via double, ma via long, allora il risultato è ancora più impressionante, poiché la somma via int (moltiplicazione e arrotondamento seguito dalla divisione della somma finale) calcola più velocemente della normale somma di 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));  
  }

risultato:

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 la somma non è via double, ma via long, allora il risultato è ancora più impressionante, perché la somma via int (moltiplicazione e arrotondamento, seguito dalla divisione della somma totale) è più veloce di una normale somma double.

risultato:

Decimale per l'aggiunta di confronto.

Link sbagliato, non è un'implementazione completa.

 
fxsaber:

E lo si fa ogni volta attraverso la normalizzazione. Beh, questo è un terribile spreco di risorse informatiche.

Perché anche se i prezzi non sono normalizzati, il controllo viene fatto semplicemente senza alcuna normalizzazione:

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

Dato che i prezzi sono multipli di ticksize

 
Nikolai Semko:
Inoltre, la normalizzazione attraverso int risulta anche essere più accurata (lo si può vedere dal numero di nove dopo l'ultima cifra della normalizzazione - evidenziata in blu).

Il test non è corretto. Perché dividete per 100000.0 solo una volta alla fine? Dovrebbe essere eseguito ad ogni iterazione e poi sommato. Questo è un confronto corretto. Ma questa non è affatto una normalizzazione - avete appena ottimizzato il vostro algoritmo di test. Naturalmente, sarà più veloce e più preciso (perché l'errore accumulato è ridotto).

 
Alexey Navoykov:

Come fa a sapere questo?

Perché puoi inserire prezzi non normalizzati nel Tester e li gestirà in modo identico.

Dopo tutto, anche se i prezzi non sono normalizzati, il controllo si fa facilmente senza alcuna normalizzazione.

Per normalizzazione intendevo in questo caso, un unico standard-algoritmo, dopo averlo applicato, è possibile confrontare direttamente i doppi di questo standard.

Quindi il tester non confronta direttamente i doppi. Lo fa attraverso NormalizeDouble, ticksize o qualcos'altro. Ma certamente non attraverso il confronto diretto dei doppi. E non è affatto razionale.

 
fxsaber:

Naturalmente, è possibile e a volte anche necessario confrontare i doppi direttamente tra loro.

Per esempio, OnTick è chiamato un trilione di volte durante Optimize. Il tester integrato, per capire se eseguire o meno una limit call in sospeso, confronta il prezzo corrente del simbolo corrispondente e il prezzo della limit call. Lo fa per ogni ordine pendente prima di ogni chiamata OnTick. Cioè questi controlli vengono fatti decine e centinaia di miliardi di volte.

E lo si fa ogni volta attraverso la normalizzazione. Beh, questo è un orribile spreco di risorse informatiche. Poiché i prezzi degli ordini pendenti e del simbolo sono preliminarmente normalizzati. Pertanto, possono e devono essere confrontati direttamente tra loro.

Il MQL-custom MQL Tester non batte il tester integrato nativo in termini di prestazioni.

Così ho deciso di controllare la versione senza senso delle prestazioni.
E il risultato è stato sorprendente.
Il confronto anche del doppio pre-normalizzato è ancora più lento in media di quando il doppio viene confrontato attraverso epsilon o conversione in 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));  
  }

Il risultato:

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

Non escludo che molto dipenda dalla novità e dall'architettura del processore, e per qualcuno il risultato può essere diverso.

A dire la verità - non capisco nemmeno perché succede.
Sembra che il compilatore non abbia nulla da ottimizzare con la somma di numeri casuali. Non puoi mettere l'arrotondamento fuori dalle parentesi.
Sembra che il confronto dei doppi nel processore sia un comando
Quando si confronta tramite epsilon (il modo più veloce) abbiamo ancora un'operazione di confronto di due doppi ma in più abbiamo una chiamata di funzione con passaggio di tre parametri e un'operazione di sottrazione.
Il rendimento dell'operazione di confronto di due doppi dipende dai valori delle variabili stesse? Ne dubito.
Cavolo, non capisco. Per favore aiutatemi, cosa non ho preso in considerazione o dove ho sbagliato?

Motivazione: