Erreurs, bugs, questions - page 2822

 
Alexey Navoykov:
C'est peut-être plus rapide, mais c'est tout simplement faux.
Passez quelque chose comme 12345.0000000000001 (similaire à votre exemple) dans votre ceil, et vous pouvez obtenir 12346 dans la sortie.

Avez-vous essayé vous-même ?
Essayez-le :

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

Sortie :

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
devrait être 12346, parce qu'il s'agit d'un plafond ("Renvoie la valeur numérique entière la plus proche de la valeur ci-dessus")
le premier cas est 12345, parce que les chiffres significatifs en type double sont 17, alors que vous avez 18
 
Nikolai Semko:

Vraiment, vous ne pouvez pas comparer les doubles. C'est une règle stricte.

Bien sûr, il est possible et parfois même nécessaire de comparer directement les doubles entre eux.

Par exemple, OnTick est parfois appelé un trillion de fois pendant l'optimisation. Afin de comprendre s'il faut ou non exécuter une limite en attente, le testeur intégré compare le prix actuel du symbole correspondant et le prix limite. Il le fait pour chaque ordre en attente avant chaque appel OnTick. C'est-à-dire que ces contrôles sont effectués des dizaines et des centaines de milliards de fois.

Et cela se fait à chaque fois par la normalisation. Eh bien, c'est un horrible gaspillage de ressources informatiques. Puisque les prix des ordres en attente et du symbole sont préalablement normalisés. Par conséquent, ils peuvent et doivent être comparés directement les uns aux autres.

Le testeur MQL personnalisé surpasse facilement le testeur natif intégré en termes de performances.

 

fxsaber
:

Bien sûr, il est possible et parfois même nécessaire de comparer directement les doubles entre eux.

Par exemple, OnTick est parfois appelé un trillion de fois pendant Optimize. Le testeur intégré, afin de comprendre s'il faut exécuter ou non un appel limite en attente, compare le prix actuel du symbole correspondant et le prix de l'appel limite. Il le fait pour chaque ordre en attente avant chaque appel OnTick. C'est-à-dire que ces contrôles sont effectués des dizaines et des centaines de milliards de fois.

Et cela se fait à chaque fois par la normalisation. Eh bien, c'est un horrible gaspillage de ressources informatiques. Puisque les prix des ordres en attente et du symbole sont préalablement normalisés. Par conséquent, ils peuvent et doivent être comparés directement les uns aux autres.

Le testeur personnalisé MQL surpasse facilement le testeur intégré natif en termes de performances.

NormalizeDouble() est une fonction très coûteuse. Par conséquent, vous feriez mieux de l'oublier.

Voici un script qui démontre la différence entre NormalizeDouble() et 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));
  }

résultat :

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 normalisation par int est également plus précise (vous pouvez le voir par le nombre de neuf après le dernier chiffre de la normalisation - surligné en bleu).
 
Nikolai Semko:

NormalizeDouble() est une fonction très coûteuse. C'est pourquoi il est préférable de l'oublier.

Voici un script qui démontre la différence entre NormalizeDouble() et normalize with int :

résultat :

SZZ la normalisation par int est encore plus précise (vous pouvez le voir par le nombre de neuf après le dernier chiffre de la normalisation - surligné en bleu).

et si la somme n'est pas faite via le double, mais via le long, alors le résultat est encore plus impressionnant, puisque la somme via le int (multiplication et arrondi suivi de la division de la somme finale) se calcule plus rapidement que la somme normale du 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));  
  }

résultat :

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:

Et si la somme n'est pas effectuée via un double, mais via un long, le résultat est encore plus impressionnant, car la somme via un int (multiplication et arrondi, suivi de la division de la somme totale) est plus rapide qu'une somme double normale.

résultat :

Décimal pour l'addition de la comparaison.

Mauvais lien, ce n'est pas une implémentation complète.

 
fxsaber:

Et cela se fait par la normalisation à chaque fois. Eh bien, c'est un terrible gaspillage de ressources informatiques.

Car même si les prix ne sont pas normalisés, le contrôle est simplement effectué sans aucune normalisation :

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

Étant donné que les prix sont des multiples du ticksize

 
Nikolai Semko:
De plus, la normalisation par int s'avère également plus précise (vous pouvez le constater par le nombre de neuf après le dernier chiffre de la normalisation - surligné en bleu).

Le test est incorrect. Pourquoi divisez-vous par 100000.0 une seule fois à la fin ? Cela devrait être fait à chaque itération et ensuite additionné. C'est une comparaison juste. Mais ce n'est pas du tout une normalisation - vous avez juste optimisé votre algorithme de test. Naturellement, il sera plus rapide et plus précis (car l'erreur accumulée est réduite).

 
Alexey Navoykov:

Comment le savez-vous ?

Parce que vous pouvez entrer des prix non normalisés dans le testeur et il les traitera de manière identique.

Après tout, même si les prix ne sont pas normalisés, la vérification se fait facilement sans aucune normalisation.

Par normalisation j'entends dans ce cas, un seul algorithme standard, après l'avoir appliqué, vous pouvez directement comparer les doubles de ce standard.

Le testeur ne compare donc pas directement les doubles. Il le fait par NormalizeDouble, ticksize ou autre chose. Mais certainement pas par une comparaison directe des doubles. Et ce n'est pas du tout rationnel.

 
fxsaber:

Bien sûr, il est possible et parfois même nécessaire de comparer directement les doubles entre eux.

Par exemple, Optimize OnTick est parfois appelé un trillion de fois. Le testeur intégré, afin de comprendre s'il faut exécuter ou non un appel limite en attente, compare le prix actuel du symbole correspondant et le prix de l'appel limite. Il le fait pour chaque ordre en attente avant chaque appel OnTick. C'est-à-dire que ces contrôles sont effectués des dizaines et des centaines de milliards de fois.

Et cela se fait à chaque fois par la normalisation. Eh bien, c'est un horrible gaspillage de ressources informatiques. Puisque les prix des ordres en attente et du symbole sont préalablement normalisés. Par conséquent, ils peuvent et doivent être comparés directement les uns aux autres.

Le testeur MQL personnalisé ne bat pas le testeur intégré natif en termes de performances.

J'ai donc décidé de vérifier la version insensée des performances.
Et le résultat était surprenant.
La comparaison, même d'un double pré-normalisé, est encore plus lente en moyenne que lorsque le double est comparé par epsilon ou par conversion en 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));  
  }

Le résultat :

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

Je n'exclus pas qu'une grande partie dépende de la nouveauté et de l'architecture du processeur, et pour quelqu'un le résultat peut être différent.

Pour vous dire la vérité, je ne comprends même pas pourquoi cela se produit.
Il semble que le compilateur n'ait rien à optimiser avec la somme des nombres aléatoires. Vous ne pouvez pas mettre les arrondis hors parenthèses.
Il semble que la comparaison double dans le processeur soit une commande
. En comparant par epsilon (la manière la plus rapide), nous avons toujours une opération de comparaison de deux doubles, mais en plus nous avons un appel de fonction avec passage de trois paramètres et une opération de soustraction.
La performance de l'opération de comparaison de deux doubles dépend-elle des valeurs des variables elles-mêmes ? J'en doute.
Bon sang, je ne comprends pas. Aidez-moi, qu'est-ce que je n'ai pas pris en compte ou qu'est-ce que j'ai fait de travers ?

 
Nikolai Semko:

J'ai décidé de vérifier la version du speed rambling.
Et le résultat était surprenant.
La comparaison, même d'un double pré-normalisé, est en moyenne encore plus lente que si le double est comparé par epsilon ou par conversion en int.

Le résultat :

Je n'exclus pas que beaucoup dépende de la nouveauté et de l'architecture du processeur et que certains programmeurs puissent obtenir un résultat différent.

Étonnamment, la fonction d'arrondi à un nombre entier renvoie un nombre réel, et non un nombre entier, vous devez également arrondir le type à un nombre entier. Où est la logique de la langue ?