Can price != price ?

 

J'essaie de comprendre quelque chose d'étrange que je vois afin de pouvoir mieux coder autour de cela à l'avenir . . .

J'ai remarqué que quelque chose d'étrange se passait avec l'un de mes indicateurs, il ne faisait pas ce qu'il aurait dû faire, j'ai vérifié le code et il semblait correct. J'ai donc fait une petite enquête et j'ai fini par créer un petit indicateur de test.

Essentiellement, cela semble être vrai ... ...

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

... une idée sur la façon dont cela se produit ?

 
Questions et réponses antérieures
 
RaptorUK:

. ... une idée de comment cela se produit ?

C'est dû au fonctionnement interne de NormalizeDouble(). Par exemple...

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

Même résultat, soit dit en passant, si vous faites ce qui suit :

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

Après l'affectation initiale, TestValue = 1.5737300000000001. NormalizeDouble(..., 5) produit 1.5737299999999999.

 
WHRoeder:
Questions et réponses antérieures
Sauf votre respect, je ne pense pas que ce message réponde à la question que j'ai posée dans ce fil de discussion.
Je sais qu'il existe d'autres façons de procéder que NormalizeDouble... Ce que je ne comprends pas, c'est pourquoi iClose renvoie une valeur qui n'est pas déjà normalisée...
 
jjc:

C'est dû au fonctionnement interne de NormalizeDouble(). Par exemple...

Même résultat, soit dit en passant, si vous faites ce qui suit :

Après l'affectation initiale, TestValue = 1.5737300000000001. NormalizeDouble(..., 5) produit 1.5737299999999999.


Alors comment faire pour que TestValue soit égal à 1.57373 et non > ou < ?
 
RaptorUK:

Alors comment faire pour que TestValue soit égal à 1.57373 et non > ou < ?
Au cas où ce ne serait pas déjà clair, 1,57373 ne peut pas être représenté exactement comme une valeur à virgule flottante. Il en va de même pour des valeurs telles que 0,1. La seule bizarrerie est que NormalizeDouble() finit par utiliser une approximation différente des autres parties du langage MQ4.
 
jjc:
Au cas où cela ne serait pas déjà clair, 1,57373 ne peut pas être représenté exactement comme une valeur à virgule flottante. Il en va de même pour des valeurs telles que 0,1. La seule bizarrerie est que NormalizeDouble() utilise une approximation différente des autres parties du langage MQ4.

Ah... non, ce n'était pas clair... je ne le savais pas. Merci, je vais faire des recherches.
 
RaptorUK:
Ah... non, ce n'était pas clair... je ne le savais pas. Merci, je vais me renseigner.

Les valeurs et l'arithmétique à virgule flottante sont rapides, car elles sont prises en charge par le processeur de l'ordinateur, mais il faut savoir que certaines valeurs ne peuvent pas être représentées précisément dans une variable à virgule flottante. (Pour un exemple des implications en termes de vitesse, voir https://www.mql5.com/en/forum/116228/page2#156859).

En effet, tout ce qui implique des doubles peut introduire une sorte d'erreur d'arrondi. Cela conduit à toutes sortes de bizarreries amusantes. Par exemple, 0,1 * 10 = 1,0, mais 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 ! = 1,0.

Il en résulte que NormalizeDouble(x, y) n'est pas exactement synonyme de Round(x, y). NormalizeDouble() renvoie l'approximation en virgule flottante la plus proche possible de la valeur arrondie. Si vous faites NormalizeDouble(a, n) == NormalizeDouble(b, n), vous dites en fait "a et b sont-ils égaux, en tenant compte du fait que l'arithmétique en virgule flottante peut introduire des différences d'arrondi à plus de n décimales ?

Comme de nombreuses personnes l'ont dit, NormalizeDouble(a, 5) == NormalizeDouble(b, 5) est donc en fait équivalent à MathAbs(a - b) < 0.00001, et ce dernier s'exécute légèrement plus rapidement. Cette dernière est également courante car elle est largement utilisée dans les langages/plateformes qui ne fournissent pas d'équivalent pratique à la fonction NormalizeDouble(). Mais la différence de performance est si minime que je m'en tiendrais à NormalizeDouble() si vous pensez que cela rend votre code plus lisible.

Tout ceci est parfaitement normal pour les langages qui ont un type de données double. La partie qui introduit une bizarrerie propriétaire et typique de MQ4 est que 1.57373 != NormalizeDouble(1.57373, 5). Il est pervers que la déclaration de la constante 1.57373 et l'utilisation de NormalizeDouble() choisissent des approximations différentes de la valeur en virgule flottante dans le meilleur des cas.

 

Merci. :-)

J'étais conscient du problème, mais pas vraiment de la raison et donc pas entièrement conscient des implications possibles.

 
sauf avec zéro, ne jamais comparer les doubles pour l'égalité
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:
sauf avec zéro, ne jamais comparer les doubles pour l'égalité


J'ai un peu plus d'une centaine de lignes de code où c'est exactement ce que je fais... et j'ai utilisé NormalizeDouble sur presque tout ce que je voyais pour que cela fonctionne de manière fiable. Je comprends l'idée derrière vos suggestions, merci, mais je pense qu'elles peuvent avoir un effet négatif sur la lisibilité de mon code et donc la facilité de modification à l'avenir.

Je modifierai ce bloc de code dans un avenir pas trop lointain pour le faire fonctionner avec des horizons temporels autres que celui du graphique sur lequel il est exécuté. Quand je ferai cela, je prévois de supprimer le NormalizeDoubles et de le remplacer par quelque chose d'autre... je ne suis pas encore sûr à 100%, peut-être une conversion en nombres entiers avant la comparaison...

Merci pour votre aide, comme d'habitude :-)