Errores, fallos, preguntas - página 2822

 
Nikolai Semko:

Sólo que el redondeo no se hace utilizando las funciones estándar round(), ceil(), floor() porque también devuelven double.

Pero a través de estos, especialmente funcionan más rápido que los normales:

Puede que sea más rápido, pero está mal.
Pase algo como 12345.0000000000001 en su ceil (similar a su ejemplo), y puede obtener 12346 en la salida
 
Alexey Navoykov:
Puede que sea más rápido, pero está mal.
Pase algo como 12345.0000000000001 (similar a su ejemplo) en su ceil, y puede obtener 12346 en la salida.

¿Lo has probado tú mismo?
Pruébalo:

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

La salida:

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
debería ser 12346, porque es un ceil ("Devuelve el valor numérico entero más cercano desde arriba")
el primer caso es 12345, porque los dígitos significativos en tipo doble son 17, mientras que tú tienes 18
 
Nikolai Semko:

Realmente, no se pueden comparar los dobles. Es una regla dura.

Por supuesto, es posible y a veces incluso necesario comparar los dobles directamente entre sí.

Por ejemplo, OnTick se llama a veces un trillón de veces durante la optimización. Para saber si se debe ejecutar o no un límite pendiente, el comprobador incorporado compara el precio actual del símbolo correspondiente y el precio del límite. Lo hace para cada orden pendiente antes de cada llamada OnTick. Es decir, estas comprobaciones se hacen decenas y cientos de miles de millones de veces.

Y se hace cada vez a través de la normalización. Bueno, esto es un horrible desperdicio de recursos informáticos. Dado que los precios de las órdenes pendientes y del símbolo están preliminarmente normalizados. Por lo tanto, pueden y deben compararse directamente entre sí.

El comprobador MQL personalizado supera fácilmente el rendimiento del comprobador nativo incorporado.

 

fxsaber
:

Por supuesto, es posible y a veces incluso necesario comparar los dobles directamente entre sí.

Por ejemplo, OnTick se llama a veces un trillón de veces durante la optimización. El comprobador incorporado, para entender si se debe ejecutar un límite pendiente o no, compara el precio actual del símbolo correspondiente y el precio del límite. Lo hace para cada orden pendiente antes de cada llamada OnTick. Es decir, estas comprobaciones se hacen decenas y cientos de miles de millones de veces.

Y se hace cada vez a través de la normalización. Bueno, esto es un horrible desperdicio de recursos informáticos. Dado que los precios de las órdenes pendientes y del símbolo están preliminarmente normalizados. Por lo tanto, pueden y deben compararse directamente entre sí.

El comprobador MQL personalizado supera fácilmente el rendimiento del comprobador nativo incorporado.

NormalizeDouble() es una función muy cara. Por lo tanto, será mejor que te olvides de ello.

Aquí hay un script que demuestra la diferencia entre NormalizeDouble() y normalizar con 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 la normalización por int es también más precisa (se puede ver por el número de nueves después del último dígito de la normalización - resaltado en azul).
 
Nikolai Semko:

NormalizeDouble() es una función muy cara. Por eso es mejor olvidarlo.

Aquí hay un script que demuestra la diferencia entre NormalizeDouble() y normalizar con int:

resultado:

SZZ la normalización por int es aún más precisa (se puede ver por el número de nueves después del último dígito de la normalización - resaltado en azul).

y si la suma no es vía double, sino vía long, entonces el resultado es aún más impresionante, ya que la suma vía int (multiplicación y redondeo seguido de la división de la suma final) calcula más rápido que la suma normal de 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));  
  }

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:

Y si la suma no es vía doble, sino vía long, entonces el resultado es aún más impresionante, porque la suma vía int (multiplicación y redondeo, seguido de la división de la suma total) es más rápida que una suma doble normal.

resultado:

Decimal para añadir a la comparación.

Enlace equivocado, no es una implementación completa.

 
fxsaber:

Y se hace a través de la normalización cada vez. Esto es un terrible desperdicio de recursos informáticos.

Porque aunque los precios no estén normalizados, la comprobación se hace simplemente sin ninguna normalización:

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

Dado que los precios son múltiplos del tamaño de la cesta

 
Nikolai Semko:
Además, la normalización a través de int también resulta ser más precisa (se puede ver por el número de nueves después del último dígito de la normalización - resaltado en azul).

La prueba es incorrecta. ¿Por qué se divide por 100000,0 sólo una vez al final? Debería realizarse en cada iteración y luego sumarse. Es una comparación justa. Pero esto no es una normalización en absoluto, sólo ha optimizado su algoritmo de prueba. Naturalmente, será más rápido y más preciso (porque se reduce el error acumulado).

 
Alexey Navoykov:

¿Cómo lo sabes?

Porque puede introducir precios no normalizados en el Comprobador y los tratará de forma idéntica.

Al fin y al cabo, aunque los precios no estén normalizados, la comprobación se hace fácilmente sin ninguna normalización.

Por normalización me refiero en este caso, a un único algoritmo estándar, después de aplicarlo, puedes comparar directamente los dobles de este estándar.

Así que el probador no compara directamente los dobles. Lo hace a través de NormalizeDouble, ticksize o alguna otra cosa. Pero ciertamente no a través de la comparación directa de los dobles. Y no es nada racional.

 
fxsaber:

Por supuesto, es posible y a veces incluso necesario comparar los dobles directamente entre sí.

Por ejemplo, Optimizar OnTick se llama a veces un trillón de veces. El comprobador incorporado, para entender si se debe ejecutar o no una llamada límite pendiente, compara el precio actual del símbolo correspondiente y el precio de la llamada límite. Lo hace para cada orden pendiente antes de cada llamada OnTick. Es decir, estas comprobaciones se hacen decenas y cientos de miles de millones de veces.

Y se hace cada vez a través de la normalización. Bueno, esto es un horrible desperdicio de recursos informáticos. Dado que los precios de las órdenes pendientes y del símbolo están preliminarmente normalizados. Por lo tanto, pueden y deben compararse directamente entre sí.

El comprobador MQL personalizado no supera en rendimiento al comprobador nativo incorporado.

Así que decidí comprobar la versión sin sentido del rendimiento.
Y el resultado fue sorprendente.
La comparación incluso de double prenormalizado es incluso más lenta en promedio que cuando se compara double a través de epsilon o de la conversión a 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));  
  }

El 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

No excluyo que dependa mucho de la novedad y la arquitectura del procesador, y para alguien el resultado puede ser diferente.

A decir verdad, ni siquiera entiendo por qué ocurre.
Parece que el compilador no tiene nada que optimizar con la suma de números aleatorios. No se puede poner el redondeo fuera de los paréntesis.
Parece que la comparación de dobles en el procesador es un comando
Al comparar a través de épsilon (la forma más rápida) seguimos teniendo una operación de comparación de dos dobles pero además tenemos una llamada a una función con paso de tres parámetros y una operación de resta.
¿Depende el rendimiento de la operación de comparación de dos dobles de los valores de las propias variables? Lo dudo.
Caramba, no lo entiendo. Por favor, ayúdame, ¿qué he dejado de tener en cuenta o en qué me he equivocado?