¿Puede el precio != el precio ?

 

Estoy tratando de entender algo extraño que estoy viendo para poder codificar mejor en el futuro....

Me di cuenta de que algo extraño sucedía con uno de mis Indicadores, no estaba haciendo lo que debía, así que comprobé el código y parecía correcto. Así que hice una pequeña investigación y terminé creando un pequeño indicador de prueba.

Esencialmente esto parece ser cierto....

double TestValue = iClose(NULL, 0, 0);
   
if(TestValue != NormalizeDouble(TestValue, Digits) )

. . . ¿alguna idea de cómo sucede esto?

 
Preguntas y respuestas anteriores
 
RaptorUK:

. . ¿alguna idea de cómo ocurre esto?

Se debe al funcionamiento interno de NormalizeDouble(). Por ejemplo...

   double TestValue = 1.57373;
   if (TestValue != NormalizeDouble(TestValue, 5)) MessageBox("WTF?");

Los mismos resultados, por cierto, si se hace lo siguiente:

   double TestValue = StrToDouble("1.57373");
   if (TestValue != NormalizeDouble(TestValue, 5)) MessageBox("WTF?");

Después de la asignación inicial, TestValue = 1.5737300000000001. NormalizeDouble(..., 5) sobre eso produce 1.57372999999999.

 
WHRoeder:
Preguntas y respuestas anteriores
Con todo respeto, creo que ese post no responde a mi problema en este hilo.
Sé que hay otras formas de hacer esto que NormalizeDouble... lo que no entiendo es por qué iClose devuelve un valor que no está ya Normalizado...
 
jjc:

Se trata del funcionamiento interno de NormalizeDouble(). Por ejemplo...

Los mismos resultados, por cierto, si haces lo siguiente:

Después de la asignación inicial, TestValue = 1.5737300000000001. NormalizeDouble(..., 5) sobre eso produce 1.57372999999999.


Entonces, ¿cómo consigo que TestValue sea igual a 1,57373 y no > o <?
 
RaptorUK:

Entonces, ¿cómo consigo que TestValue sea igual a 1,57373 y no > o <?
Por si no está claro, 1,57373 no puede representarse exactamente como un valor de coma flotante. Lo mismo ocurre con valores como 0,1. La única rareza es que NormalizeDouble() termina usando una aproximación diferente a otras partes del lenguaje MQ4.
 
jjc:
Por si no está claro, 1,57373 no puede representarse exactamente como un valor de coma flotante. Lo mismo ocurre con valores como 0,1. La única rareza es que NormalizeDouble() termina usando una aproximación diferente a otras partes del lenguaje MQ4.

Ah... no estaba claro... no lo sabía. Gracias, voy a investigar.
 
RaptorUK:
Ah... no estaba claro... no lo sabía. Gracias, lo investigaré.

Los valores de punto flotante y la aritmética son rápidos, porque hay soporte para ellos integrado en el procesador del ordenador, pero con la contrapartida de que algunos valores no pueden ser representados con precisión en una variable de punto flotante. (Para ver un ejemplo de las implicaciones de la velocidad, consulte https://www.mql5.com/en/forum/116228/page2#156859).

En efecto, cualquier cosa que implique dobles puede introducir una especie de error de redondeo. Esto da lugar a todo tipo de peculiaridades divertidas. Por ejemplo, 0,1 * 10 = 1,0, pero 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 != 1,0

El resultado de esto es que NormalizeDouble(x, y) no es exactamente sinónimo de Round(x, y). NormalizeDouble() devuelve la aproximación en coma flotante más cercana posible al valor redondeado. Si se hace NormalizeDouble(a, n) == NormalizeDouble(b, n) entonces se está diciendo básicamente "¿son a y b iguales, teniendo en cuenta el hecho de que la aritmética de punto flotante puede introducir diferencias de redondeo en más de n decimales?".

Como mucha gente ha dicho, NormalizeDouble(a, 5) == NormalizeDouble(b, 5) es por lo tanto en efecto equivalente a MathAbs(a - b) < 0.00001, y este último se ejecuta ligeramente más rápido. Esta última también es común porque se utiliza ampliamente en lenguajes/plataformas que no proporcionan un equivalente práctico a la función NormalizeDouble(). Pero la diferencia de rendimiento es tan pequeña que yo me quedaría con NormalizeDouble() si crees que hace tu código más legible.

Todo esto es perfectamente normal para los lenguajes que tienen un tipo de datos doble. La parte que introduce alguna peculiaridad propia y típica de MQ4 es que 1.57373 != NormalizeDouble(1.57373, 5). Es perverso que al declarar la constante 1.57373 frente a usar NormalizeDouble() se elijan diferentes aproximaciones de punto flotante del valor en el mejor de los casos.

 

Gracias. :-)

Estaba al tanto de la cuestión pero no sabía muy bien el motivo y, por tanto, no era plenamente consciente de las posibles implicaciones.

 
excepto con cero nunca comparar dobles para la igualdad
if (a > b)
if (a - b > Point / 2.)
if (a >= b)
if (a - b > -Point)
if (a != b)
if (MathAbs(a - b) > Point / 2.)
 
WHRoeder:
excepto con cero nunca comparar dobles para la igualdad


Tengo algo más de cien líneas de código en las que estoy haciendo exactamente eso . . y he utilizado NormalizeDouble en casi todo lo que se ve para conseguir que funcione de forma fiable. Entiendo la idea detrás de tus sugerencias, gracias, pero creo que pueden tener un efecto negativo en la legibilidad de mi código y por lo tanto la facilidad de modificación en el futuro.

Voy a modificar este bloque de código en un futuro no muy lejano para que funcione con marcos temporales distintos del marco temporal del gráfico en el que se está ejecutando. Cuando llegue a hacer esto planeo erradicar el NormalizeDoubles y sustituirlo por otra cosa... no estoy 100% seguro todavía, tal vez una conversión a enteros antes de la comparación...

Gracias por la ayuda, como siempre :-)