O preço da lata != preço ?

 

Estou tentando entender algo estranho que estou vendo para que possa codificar melhor no futuro...

Notei algo estranho acontecendo com um dos meus Indicadores, ele não estava fazendo o que deveria ter feito, então verifiquei o código e parecia correto. Então, fiz uma pequena investigação e acabei criando um pequeno Indicador de teste.

Essencialmente isto parece ser verdade...

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

. qualquer idéia de como isto acontece ?

 
Previamente perguntado e respondido
 
RaptorUK:

. . qualquer idéia de como isso acontece ?

Resume-se ao funcionamento interno da NormalizeDouble(). Por exemplo...

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

Os mesmos resultados, a propósito, se você fizer o seguinte:

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

Após a atribuição inicial, TestValue = 1,5737300000000001. NormalizeDouble(..., 5) sobre o que produz 1,573729999999999999.

 
WHRoeder:
Previamente perguntado e respondido
Com todo respeito, não creio que esse post responda ao meu problema neste tópico.
Sei que existem outras maneiras de fazer isto além de NormalizeDouble . . o que não entendo é porque iClose está devolvendo um valor que ainda não está Normalizado .
 
jjc:

Resume-se ao funcionamento interno da NormalizeDouble(). Por exemplo...

Os mesmos resultados, a propósito, se você fizer o seguinte:

Após a atribuição inicial, TestValue = 1,5737300000000001. NormalizeDouble(..., 5) sobre o que produz 1,573729999999999999.


Então, como faço para que o TestValue seja igual a 1,57373 não > ou < ?
 
RaptorUK:

Então, como faço para que o TestValue seja igual a 1,57373 não > ou < ?
Caso isto ainda não esteja claro, 1,57373 não pode ser representado exatamente como um valor de ponto flutuante. O mesmo é válido para valores como 0,1. A única singularidade é que NormalizeDouble() acaba usando uma aproximação diferente para outras partes da linguagem MQ4.
 
jjc:
Caso isto ainda não esteja claro, 1,57373 não pode ser representado exatamente como um valor de ponto flutuante. O mesmo é válido para valores como 0,1. A única singularidade é que NormalizeDouble() acaba usando uma aproximação diferente para outras partes da linguagem MQ4.

Ah . não, não estava claro . . eu não sabia disso. Obrigado, eu vou investigar.
 
RaptorUK:
Ah . não, não estava claro ... Não sabia disso. Obrigado, eu vou investigar.

Valores de ponto flutuante e aritmética são rápidos, porque há suporte para eles embutidos diretamente no processador do computador, mas com o trade-off de que alguns valores não podem ser representados com precisão em uma variável de ponto flutuante. (Para um exemplo das implicações de velocidade, veja https://www.mql5.com/en/forum/116228/page2#156859).

Com efeito, qualquer coisa que envolva duplicação pode introduzir uma espécie de erro de arredondamento. Isto leva a todo tipo de caprichos divertidos. Por exemplo 0,1 * 10 = 1,0, mas 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 != 1,0

O resultado disto é que NormalizeDouble(x, y) não é exatamente sinônimo de Round(x, y). NormalizeDouble() retorna a aproximação do ponto flutuante mais próximo possível do valor arredondado. Se você faz NormalizeDouble(a, n) == NormalizeDouble(b, n) então você está basicamente dizendo "são a e b iguais, permitindo o fato de que a aritmética de ponto flutuante pode introduzir diferenças de arredondamento em mais de n casas decimais...".

Como muitas pessoas disseram, NormalizeDouble(a, 5) == NormalizeDouble(b, 5) é, portanto, de fato equivalente a MathAbs(a - b) < 0,00001, e este último executa um pouco mais rápido. Este último também é comum porque é amplamente utilizado em idiomas/plataformas que não fornecem um equivalente prático para a função NormalizeDouble(). Mas a diferença de desempenho é tão pequena que eu ficaria com NormalizeDouble() se você achar que isso torna seu código mais legível.

Tudo isso é perfeitamente normal para idiomas que têm um duplo tipo de datatype. O bit que introduz alguma peculiaridade proprietária e típica do MQ4 é que 1,57373 != NormalizeDouble(1,57373, 5). É perverso que declarando a constante 1,57373 versus usando NormalizeDouble() escolha diferentes aproximações de ponto flutuante do valor na melhor das hipóteses.

 

Obrigado. :-)

Eu estava ciente da questão, mas não estava bem ciente da razão e, portanto, não estava plenamente consciente das possíveis implicações.

 
exceto com zero nunca compare as duplas para igualdade
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:
exceto com zero nunca compare pares para igualdade


Tenho pouco mais de cem linhas de código onde estou fazendo exatamente isso ... e tenho usado o NormalizeDouble em quase tudo à vista para fazê-lo funcionar de forma confiável. Compreendo a idéia por trás de suas sugestões, obrigado, mas acho que elas podem ter um efeito negativo na legibilidade do meu código e, portanto, na facilidade de modificação no futuro.

Estarei modificando este bloco de código em um futuro não distante para que ele funcione com prazos diferentes dos do gráfico em que está rodando. Quando eu fizer isso, pretendo erradicar o NormalizeDoubles e substituí-lo por algo mais . . . ainda não 100% certo, talvez uma conversão para inteiros antes da comparação .

Obrigado pela ajuda, como de costume :-)