Can price != price ?

To add comments, please log in or register
Simon Gniadkowski
17216
Simon Gniadkowski  

I'm trying to understand something strange that I am seeing so I can better code round it in the future . . .

I noticed something strange happening with one of my Indicators, it wasn't doing what it should have been so I checked the code and it looked correct. So I did a little investigation and ended up creating a little test Indicator.

Essentially this seems to be true . . .

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

. . . any idea how this happens ?

William Roeder
20249
William Roeder  
Previously asked and answered
JC
1647
JC  
RaptorUK:

. . . any idea how this happens ?

It's down to the internal workings of NormalizeDouble(). For example...

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

Same results, incidentally, if you do the following:

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

After the initial assignment, TestValue = 1.5737300000000001. NormalizeDouble(..., 5) on that produces 1.5737299999999999.

Simon Gniadkowski
17216
Simon Gniadkowski  
WHRoeder:
Previously asked and answered
With respect, I don't think that post answers my issue in this thread.
I know there are other ways of doing this than NormalizeDouble . . . what I don't understand is why iClose is returning a value that is not already Normalized . .
Simon Gniadkowski
17216
Simon Gniadkowski  
jjc:

It's down to the internal workings of NormalizeDouble(). For example...

Same results, incidentally, if you do the following:

After the initial assignment, TestValue = 1.5737300000000001. NormalizeDouble(..., 5) on that produces 1.5737299999999999.


So how do I get TestValue to be equal to 1.57373 not > or < ?
JC
1647
JC  
RaptorUK:

So how do I get TestValue to be equal to 1.57373 not > or < ?
In case this isn't already clear, 1.57373 can't be represented exactly as a floating point value. The same is true of values such as 0.1. The only oddity is that NormalizeDouble() ends up using a different approximation to other parts of the MQ4 language.
Simon Gniadkowski
17216
Simon Gniadkowski  
jjc:
In case this isn't already clear, 1.57373 can't be represented exactly as a floating point value. The same is true of values such as 0.1. The only oddity is that NormalizeDouble() ends up using a different approximation to other parts of the MQ4 language.

Ah . . no it wasn't clear . . I didn't know that. Thanks, I'll investigate.
JC
1647
JC  
RaptorUK:
Ah . . no it wasn't clear . . I didn't know that. Thanks, I'll investigate.

Floating point values and arithmetic are fast, because there's support for them built right into the computer's processor, but with the trade-off that some values cannot be precisely represented in a floating point variable. (For an example of the speed implications, see https://www.mql5.com/en/forum/116228/page2#156859).

In effect, anything at all involving doubles can introduce a sort of rounding error. This leads to all sorts of fun quirks. For example 0.1 * 10 = 1.0, but 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 != 1.0

The result of this is that NormalizeDouble(x, y) is not exactly synonymous with Round(x, y). NormalizeDouble() returns the closest possible floating-point approximation to the rounded value. If you do NormalizeDouble(a, n) == NormalizeDouble(b, n) then you are basically saying "are a and b equal, allowing for the fact that floating point arithmetic may introduce rounding differences at more than n decimal places?".

As many people have said, NormalizeDouble(a, 5) == NormalizeDouble(b, 5) is therefore in effect equivalent to MathAbs(a - b) < 0.00001, and the latter executes slightly faster. The latter is also common because it's widely used in languages/platforms which don't provide a handy equivalent to the NormalizeDouble() function. But the performance difference is so tiny that I'd stick with NormalizeDouble() if you feel that it makes your code more readable.

All this is perfectly normal for languages which have a double datatype. The bit which does introduce some proprietary and typical MQ4 quirkiness is that 1.57373 != NormalizeDouble(1.57373, 5). It is perverse that declaring the constant 1.57373 versus using NormalizeDouble() choose different best-case floating-point approximations of the value.

Simon Gniadkowski
17216
Simon Gniadkowski  

Thank you. :-)

I was aware of the issue but not quite aware of the reason and hence not fully aware of the possible implications.

William Roeder
20249
William Roeder  
except with zero never compare doubles for equality
if (a > b)
if (a - b > Point / 2.)
if (a >= b)
if (a - b > -Point)
if (a != b)
if (MathAbs(a - b) > Point / 2.)
Simon Gniadkowski
17216
Simon Gniadkowski  
WHRoeder:
except with zero never compare doubles for equality


I have just over a hundred lines of code where I'm doing exactly that . . . and have used NormalizeDouble on almost everything in sight to get it to work reliably. I understand the idea behind your suggestions, thank you, but I think they may have negative effect on the readability of my code and hence the ease of modification in future.

I will be modifying this block of code in the not to distant future to make it work with timeframes other than the timeframe of the chart it is running on. When I come to do this I plan to eradicate the NormalizeDoubles and replace with something else . . . not 100% sure yet, maybe a conversion to integers prior to the comparison . .

Thanks for the help, as usual :-)

To add comments, please log in or register