Pregunta a los maestros del MQL4. De nuevo sobre la doble comparación. - página 6

 
VBAG:
...
La belleza del código de Irtron es su compacidad (absolutamente nada extra - ¡incluso las variables se guardan!)
...


¡¡¡¡Aquí, mira el método propuesto por Irtron

int ComparePrice(double a, double b, double digit)
{
     a -= b;
     b = digit;
     if (a > b)
         return (1);
     if (a < -b)
         return (-1);
     return (0);
 }

Es compacto y más rápido que el mío, pero incluso a primera vista parece sospechoso, porque la comparación implica dos variables doble!!!!

En este esquema, sólo el dígito actúa como una constante y puede ser comparado, mientras que la variable a, que también es comparada, ¡permanece doble sin normalizar!
¿Despierta esto alguna sospecha? (Bajo constante entiendo las constantes habituales - "#define" y aquellas variables que no estaban involucradas en las operaciones).

¡¡¡También en otras ramas los propios desarrolladores escribieron que hasta las constantes dobles mejor no compararlas!!!
¡Tampoco es correcto hacer NormalizeDouble(a) ! NormalizarDoble(b), !OC! - ¡operador de comparación!

Además, en la versión original en lugar de dígitos constantes era así b = Punto / 2 - aquí ya dos de dos variables no normalizadas?

Me gustaría creer que esta variante es una genialidad, ¡pero primero disipa mis dudas!

¿Tal vez alguien encuentre errores en mi variante también?
 
gravity001:

He aquí el método sugerido por Irtron

int ComparePrice(double a, double b, double digit)
{
     a -= b;
     b = digit;
     if (a > b)
         return (1);
     if (a < -b)
         return (-1);
     return (0);
 }
Sugerí un método diferente.
Mira más de cerca, por favor.


Sobre todo porque en la versión original en lugar de la constante de dígitos era así b = Punto / 2 - aquí ya dos de las dos variables no normalizadas?

¿De qué versiones estamos hablando?

Ya te he hablado de la normalización. Primero, dime por qué aplicarlo, y luego cómo y dónde.

Por ejemplo, tal vez sepa por qué debemos comparar los precios con una precisión de 14 dígitos, que se menciona como un logro en la discusión anterior. :) Te recuerdo que la función que sugerí se llama ComparePrice :)
 

Irtron писал (а):
...
Sugerí un método diferente.
Mira con más atención, por favor.
...

¿De qué versiones estamos hablando?

Ya te he hablado de la normalización. Primero, dígame por qué debe aplicarse, y luego cómo y dónde.

Por ejemplo, tal vez sepa por qué debemos comparar los precios con una precisión de 14 dígitos, que se menciona como una especie de logro en la discusión anterior. :) Te recuerdo que la función que sugerí se llama ComparePrice :)
Toma, ¿es esto tuyo? ¿Estoy citando correctamente ahora?
Irtron 10.09.2007 04:07

...

int CompararPrecio(doble a, doble b)
{
a -= b;
b = Punto / 2;
si (a > b)
volver (1);
si (a < -b)
devolver (-1);
retorno (0);
}
...
Sólo un recordatorio, la función que sugerí se llama ComparePrice. :)

Si te fijas, también he citado la función llamada ComparePrice. Es que el tuyo ya ha sido modificado por VBAG. Por eso me he referido a la versión prístina refiriéndome a la original, es decir, a su función.
Yo mismo he probado ambas funciones. Sí, resultaron ser más rápidos. Pero, ¿cómo comprobar la fiabilidad de la comparación? Estoy muy confundido por la comparación de dos variables dobles. ¡Aunque todo debe ser correcto ya que lleva un intervalo! Pero todavía hay sospechas de que no siempre funciona correctamente.

Ya he hablado de la normalización. Primero dime por qué aplicarlo, y luego cómo y dónde.

Esta es la pregunta clave, ¿verdad? Yo mismo lo he pensado durante mucho tiempo: "Se escribe el doble y se obtiene el doble ".
No he encontrado la respuesta exacta. Pero me lo imagino así

doble a = 2.000000000000
doble b = 2.000000000001
doble c = 1,999999999999

Todas estas variables son diferentes y se almacenan en la memoria con precisión hasta el último dígito.
En este caso, nosotros mismos definimos los signos (dígitos). Todo lo que no está definido se llena de ceros.

Si hubiéramos definido el doble a = 2,0, y está almacenado en memoria como 2,0000001 o 1,9999999, está claro que NormalizeDouble() no ayudaría, ¡porque devolvería un valor inexacto!
Creo que ese error no se produce casi nunca al memorizar el valor de una variable. Además, no creo que el número 2,0 se almacene como 1,999999999999999 a propósito, ya que cada carácter (dígito o punto) se almacena con un bit específico en la cadena de bits. Por lo tanto, el número 2,0 se almacena con seguridad como 2,00000...00.

El otro caso es cuando no determinamos los signos nosotros mismos:

a = 4.0;
b = 2.0;
c = a / b // - la operación de "división" la realiza el procesador, o más bien el coprocesador, y rellena el premenente con caracteres (dígitos).

Después de la operación puede ser:
Lo más común:
с = 2.000...0
с= 1.99999999...
с= 2.00000001...

es decir, el resultado suele diferir del valor real en una pequeña cantidad.

Los errores grandes ocurren muy raramente:
с = 2.3

Aquí hay dos explicaciones:
1) parte de la cadena de bits se vio afectada en la memoria al llamar a o b, es decir, las variables a y b se modificaron.
2) se ha producido un error durante la operación de "división".

Creo que la 2) es la más frecuente. Por qué no lo sé. Creo que tiene que ver con el hecho de que el coprocesador está pensado para ser altamente optimizado en detrimento de la inutilidad.

Al comparar una variable con el número 2.000...00, la igualdad obviamente fallará. No todos los bits serán iguales.

Ahora, NormalizeDouble() está aquí para ayudar.
NormalizeDouble() "arreglará" este pequeño error.
Como el error suele ser muy pequeño, el redondeo con una precisión pequeña siempre dará el resultado correcto.

Míralo de esta manera:
Redondea el número a = 2,111...11 al segundo dígito.
NormalizeDouble() escribirá 2,11 en una nueva variable y rellenará los bits restantes con ceros, ¡no con unos!
Creo que se verá así:

double MyNormalizeDouble(double value, int digits)
{
    int factor = MathRound( MathPow(10, digits) ); // factor - это множитель,
                                                      с помощью которого мы из VALUE сделаем целое число
    double result = MathRound(factor * value) / factor;
    
    return(result);
}
Aquí, he intentado explicar lo mejor posible por qué se necesita NormalizeDouble().

Hasta hace poco estaba completamente satisfecho con esta explicación, pero recientemente me he convencido de que este esquema no siempre funciona

NormalizeDouble(a, 2) !OC! NormalizeDouble(b, 2) donde !OC! - es un operador de comparación.
Aunque, según tengo entendido, ¡siempre debe funcionar!
Por lo tanto, ¡estaré encantado de recibir cualquier crítica razonada y comprensible!
 
gravity001:

¡¡¡Además, en otros hilos los propios desarrolladores han escrito que incluso las constantes dobles es mejor no compararlas!!!
¡Esto es nuevo para mí! ¡Esto es lo que se llama una pregunta sustantiva!
Si puede, por favor, póngame un enlace.

Tengo una pregunta para los desarrolladores:

Por favor, explique cuáles son las limitaciones o posibles problemas al comparar dobles utilizando constantes:
1.
doble a=1,23456789;
doble b;

if(a>b) o if(a<b)

Y de esta forma:
2.
#define a 1.23456789;

doble b;

if(a>b) o if(a<b)
 
gravity001:

Sobre todo porque la versión original tenía b = Punto / 2 en lugar de dígitos constantes - aquí ya dos de dos variables no normalizadas?

Por eso he sustituido b = Punto / 2 por la constante (1.menos operaciones - mayor velocidad 2.transferencia explícita de la constante - mayor fiabilidad)

Pero a la luz de tu afirmación sobre la falta de fiabilidad de la comparación de la doble constante, se pierde todo el sentido. Tenemos que estudiar esta cuestión con más detenimiento.

Me pregunto qué dirán los desarrolladores.
 
VBAG писал (а):
...
Eso sí que es nuevo para mí. ¡Eso es lo que se llama una pregunta sustantiva!
Si puede, por favor, póngame un enlace.
...
Sí, estaba buscando el enlace, quería pegarlo inmediatamente, ¡pero no lo encontré! Recuerdo haberlo visto en algún sitio, pero había muchos temas de este tipo. También he leído un montón de temas en otros foros, y de libros sobre el tema.
Recuerdo que alguien escribió en alguna parte, pero no puedo recordar dónde(((((. Por lo tanto, probablemente, no fue correcto de mi parte escribir: "en otros hilos que los propios desarrolladores escribieron"
Me disculpo.
Pero si encuentro el enlace no dudes en publicarlo.

Creo que lo leí en un libro sobre C++. Describía cómo comparar números reales y decía que lo mejor era ir a los enteros.
 
gravity001:
VBAG escribió (a):
...
¡Esto es nuevo para mí! ¡Eso es lo que se llama una pregunta sustantiva!
Si puede, por favor, póngame un enlace.
...
Sí, estaba buscando el enlace, quería insertarlo inmediatamente, ¡pero no lo encontré! Recuerdo haberlo visto en algún sitio, pero había muchos temas de este tipo. También he leído un montón de temas en otros foros, y de libros sobre el tema.
Recuerdo que alguien escribió en alguna parte, pero no puedo recordar dónde(((((. Por lo tanto, probablemente, no fue correcto de mi parte escribir: "en otros hilos que los propios desarrolladores escribieron"
Me disculpo.
Pero si encuentro el enlace no dudes en publicarlo.

Creo que lo leí en un libro sobre C++. Describía cómo comparar números reales y decía que lo mejor era ir a los enteros.
Gracias por su participación y ayuda. Por desgracia, no tengo formación académica en programación. Así que tengo que escuchar y memorizar más. Y espero que los desarrolladores respondan y aclaren mi pregunta.
Tengo una pregunta para los desarrolladores:

Por favor, aclare cuáles son las limitaciones o los posibles problemas al comparar dubs utilizando constantes:
1.
doble a=1,23456789;
doble b;

if(a>b) o if(a<b)

Y de esta forma:
2.
#define a 1.23456789;

doble b;

if(a>b) o if(a<b)
 
Tales problemas - 1,3333+0,0004 != 1,3337
 

Esta conversación parece prolongarse indefinidamente. Para cuando un nuevo usuario ha adquirido la experiencia y los conocimientos adecuados, suele conseguir chocar con la normalización varias veces.

Tal vez, en MT5 tenga sentido limitar forzosamente la precisión de los números reales en las operaciones de comparación, por ejemplo, a 8 decimales (es decir, forzar la ejecución de NormalizeDouble() con digit=8). Y sólo si se especifica explícitamente la función NormalizeDouble(), realizar la normalización de acuerdo con los parámetros especificados en ella. En este caso, la cuestión se planteará con mucha menos frecuencia, es decir, sólo cuando el usuario necesita exactamente la precisión especificada. En mi opinión, esta polla es un poco, pero todavía más dulce que un rábano.

 
VBAG:
¡Hola!
Como sabes, no sólo la corrección de los cálculos, sino también la fiabilidad del código que has escrito depende del estilo de programación y de la precisión del código.
No escribimos juguetes y, por lo tanto, la fiabilidad del funcionamiento del programa escrito es el primer requisito. La mayoría de los cálculos se realizan en dubles y una correcta comparación en el código de
de dos números reales en el código del programa requiere un cierto enfoque y precisión.
Estoy tratando de averiguar el estilo de programación "correcto", de ahí la pregunta:

Para una expresión

doble a;
doble b;

if(a==b) o if(a!=b)
{......} {.... ..}

los desarrolladores recomiendan esto
//+------------------------------------------------------------------+
//| Función para comparar dos números reales. ||
//+------------------------------------------------------------------+
bool CompareDouble(double Número1, double Número2)
{
bool Compare = NormalizeDouble(Number1 - Number2, 8) == 0;
return(Compare);
}
//+------------------------------------------------------------------+


¿Es correcto este código?

doble a;
doble b;

if(a>b) if(a<b)
{......} {......}


Lo más probable es que no en el caso general. ¿Cuál es la forma correcta de comprobarlo?
En general, ¿qué estilo de trabajo con los doblajes es más apropiado?
Gracias de antemano a todos los que han respondido.

Has hecho un lío... :)

La comparación de números flotantes se realiza comparando el módulo de diferencia con un pequeño umbral.

Devuelve (fabs(d1-d2) < 1e-10) por ejemplo.

¿Qué sentido tiene enredar las aguas... La función NormalizeDouble(...) es sólo para obtener informes de aspecto agradable.