- double variable with wrong decimal cases
- The Efficiency of MQL5 Indicators
- How can I trade stocks on Metatrader?
That's exactly what I was thinking.
Thanks again!
There are several ways to compare doubles, but be careful when working with (int)s because it's super-easy to mess up and accidentally cast double to int at the wrong place. int(1.999999) = 1 not 2 ...not to mention the performance difference is so negligible it's not worth the keystrokes for the extra code.
This is what metaquotes recommends...
(NormalizeDouble(num1-num2,digits)==0.0)
I like this better...
(fabs(num1-num2) < step)
This is what I actually use (CDouble library)
CDouble num1 = 3.14, num2 = 3.1399999999999; Print(num1 == num2) //overloaded operator - automatically compares doubles the right way. No need for extra code. //true
This is what your code would look like...
#include <Double.mqh> void OnStart() { CDouble lotsize(PRECISION_LOT_STEP); lotsize = 0.01; for(int i=0; i<10; i++) { lotsize += lotsize.Step(); printf("[%d] = %s", i, lotsize.ToString()); } }
KS 0 14:31:59.745 Foo (EURUSD,M1) [0] = 0.02 LN 0 14:31:59.748 Foo (EURUSD,M1) [1] = 0.03 LH 0 14:31:59.748 Foo (EURUSD,M1) [2] = 0.04 HR 0 14:31:59.748 Foo (EURUSD,M1) [3] = 0.05 HL 0 14:31:59.748 Foo (EURUSD,M1) [4] = 0.06 LG 0 14:31:59.748 Foo (EURUSD,M1) [5] = 0.07 DQ 0 14:31:59.748 Foo (EURUSD,M1) [6] = 0.08 PK 0 14:31:59.748 Foo (EURUSD,M1) [7] = 0.09 QF 0 14:31:59.748 Foo (EURUSD,M1) [8] = 0.1 CP 0 14:31:59.748 Foo (EURUSD,M1) [9] = 0.11
Just for fun I ran some performance tests comparing doubles to the eighth digit.
Method 1:
bool IsEqual1(const double num1,const double num2) { return (NormalizeDouble(num1-num2,8)==0.0); }
1: 936 ms for 100000000 iterations
Method 2:
bool IsEqual2(const double num1,const double num2) { return (fabs(num1-num2) < 1e-08); }
2: 235 ms for 100000000 iterations
Method 3: note - <int> was not appropriate for rounding since it couldn't hold the amount of data required for storing (doubles/step). Had to switch to <long>
bool IsEqual3(const double num1,const double num2) { long tick1 = (long)round(num1 / 1e-08); long tick2 = (long)round(num2 / 1e-08); return (tick1==tick2); }
3: 1531 ms for 100000000 iterations
void OnStart() { ulong iterations = (int)pow(10,8); double num1 = 0.14; double num2 = 0.1399999999999; /////////////////////////////////////////////////////////////////// long not_equal=0; ulong m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) if(!IsEqual1(num1*(double)i,num2*(double)i)) not_equal++; m=GetMicrosecondCount()-m; m/=1000; printf("1: %d ms for %d iterations",m,iterations); printf("not equal = %d",not_equal); /////////////////////////////////////////////////////////////////// not_equal=0; m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) if(!IsEqual2(num1*(double)i,num2*(double)i)) not_equal++; m=GetMicrosecondCount()-m; m/=1000; printf("2: %d ms for %d iterations",m,iterations); printf("not equal = %d",not_equal); /////////////////////////////////////////////////////////////////// not_equal=0; m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) if(!IsEqual3(num1*(double)i,num2*(double)i)) not_equal++; m=GetMicrosecondCount()-m; m/=1000; printf("3: %d ms for %d iterations",m,iterations); printf("not equal = %d",not_equal); } //+------------------------------------------------------------------+ bool IsEqual1(const double num1,const double num2) { return (NormalizeDouble(num1-num2,8)==0.0); } bool IsEqual2(const double num1,const double num2) { return (fabs(num1-num2) < 1e-08); } bool IsEqual3(const double num1,const double num2) { long tick1 = (long)round(num1 / 1e-08); long tick2 = (long)round(num2 / 1e-08); return (tick1==tick2); }
I realized that the tests weren't apples to apples since method two wasn't properly rounding. Here is a straight up apples to apples function operation comparison, and the performance results turned out the exact same as before.
void OnStart() { ulong iterations = (int)pow(10,8); double num1 = 0.14; double num2 = 0.1399999999999; /////////////////////////////////////////////////////////////////// long not_equal=0; ulong m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) if(!IsEqual1(num1*(double)i,num2*(double)i,8)) not_equal++; m=GetMicrosecondCount()-m; m/=1000; printf("1: %d ms for %d iterations",m,iterations); printf("not equal = %d",not_equal); /////////////////////////////////////////////////////////////////// not_equal=0; m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) if(!IsEqual2(num1*(double)i,num2*(double)i,8)) not_equal++; m=GetMicrosecondCount()-m; m/=1000; printf("2: %d ms for %d iterations",m,iterations); printf("not equal = %d",not_equal); /////////////////////////////////////////////////////////////////// not_equal=0; m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) if(!IsEqual3(num1*(double)i,num2*(double)i,8)) not_equal++; m=GetMicrosecondCount()-m; m/=1000; printf("3: %d ms for %d iterations",m,iterations); printf("not equal = %d",not_equal); } //+------------------------------------------------------------------+ bool IsEqual1(const double num1,const double num2,const int digits) { return (NormalizeDouble(num1-num2,digits)==0.0); } bool IsEqual2(const double num1,const double num2,const int digits) { double point = 1.0 / pow(10, digits+1); return (fabs(num1-num2) < point*5); } bool IsEqual3(const double num1,const double num2,const int digits) { double point = 1.0 / pow(10, digits); return ((long)round(num1 / point) == (long)round(num2 / point)); }
Method 1:
Method 2:
Method 3: note - <int> was not appropriate for rounding since it couldn't hold the amount of data required for storing (doubles/step). Had to switch to <long>
- An "int", can easily accommodate price quotes in ticks or points, and can certainly accommodate volume/lots in lot-step. There is no need to use "long".
- Your performance tests are somewhat flawed, because the conversion is not continuously repeated, but only done once or twice per bar or tick, because the rest of the calculations are all done in pure integer as apposed to in floating point. I can certainly attest to the increase in efficiency and performance of an EA coded for "int" instead of "double" from experience - It is not just a "theory"!
- An "int", can easily accommodate price quotes in ticks or points, and can certainly accommodate volume/lots in lot-step. There is no need to use "long".
- Your performance tests are somewhat flawed, because the conversion is not continuously repeated, but only done once or twice per bar or tick, because the rest of the calculations are all done in pure integer as apposed to in floating point. I can certainly attest to the increase in efficiency and performance of an EA coded for "int" instead of "double" from experience - It is not just a "theory"!
2. This is incorrect. These aren't compiler tricks as you allude to in point #2. I've altered the test to make for a non-biased/scientifically-fair comparison of three different rounding methods including the built-in NormalizeDouble. Even when I switch method #3 back to <int> (*not universally usable for doubles) it is still slower by a factor of 2.
New testing method: IsEqual1(price[rand()%2],price[rand()%2],_Digits)
1: 1292 ms for 100000000 iterations 2: 744 ms for 100000000 iterations 3: 1455 ms for 100000000 iterations
void OnStart() { ulong iterations = (int)pow(10,8); MqlTick tick; SymbolInfoTick(_Symbol,tick); double price[2]; price[0] = tick.bid; price[1] = tick.ask; srand(_RandomSeed); /////////////////////////////////////////////////////////////////// long not_equal=0; ulong m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) if(!IsEqual1(price[rand()%2],price[rand()%2],_Digits)) not_equal++; m=GetMicrosecondCount()-m; m/=1000; printf("1: %d ms for %d iterations",m,iterations); printf("not equal = %d",not_equal); /////////////////////////////////////////////////////////////////// not_equal=0; m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) if(!IsEqual2(price[rand()%2],price[rand()%2],_Digits)) not_equal++; m=GetMicrosecondCount()-m; m/=1000; printf("2: %d ms for %d iterations",m,iterations); printf("not equal = %d",not_equal); /////////////////////////////////////////////////////////////////// not_equal=0; m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) if(!IsEqual3(price[rand()%2],price[rand()%2],_Digits)) not_equal++; m=GetMicrosecondCount()-m; m/=1000; printf("3: %d ms for %d iterations",m,iterations); printf("not equal = %d",not_equal); } //+------------------------------------------------------------------+ bool IsEqual1(const double num1,const double num2,const int digits) { return (NormalizeDouble(num1-num2,digits)==0.0); } bool IsEqual2(const double num1,const double num2,const int digits) { double point = 1.0 / pow(10, digits+1) * 5; return (fabs(num1-num2) < point); } bool IsEqual3(const double num1,const double num2,const int digits) { double point = 1.0 / pow(10, digits); return ((int)round(num1 / point) == (int)round(num2 / point)); }
1. Sometimes it is - provided you only require minimum precision. If you are calculating slopes or something similar which requires a higher precision then no, it is not adequate.
2. This is incorrect. These aren't compiler tricks as you allude to in point #2. I've altered the test to make for a non-biased/scientifically-fair comparison of three different rounding methods including the built-in NormalizeDouble. Even when I switch method #3 back to <int> (*not universally usable for doubles) it is still slower by a factor of 2.
New testing method: IsEqual1(price[rand()%2],price[rand()%2],_Digits)
I am afraid I do not agree, but I also don't feel like getting into greater detail about it either. Lets just agree to disagree!
I am afraid I do not agree, but I also don't feel like getting into greater detail about it either. Lets just agree to disagree!
I would normally agree... sometimes personalities clash and people have opinions and sometimes you have to agree to disagree, but this is not an opinion-based matter -- it is a scientific test backed up by scientific evidence. I agree with the evidence that method #2 is by far the most efficient method for comparing doubles.
EDIT* I'd like to add, if you have any evidence to the contrary, I will happily change my mind! :)Yes, I do have evidence to the contrary and I have tried to steer you in that direction, by stating that your test is doing continuous conversions, when that is not the case of how it is implemented. Sometimes a real-life implementation can be very different to a "lab" simulation.
It is not a question of clashing personalities. It is just that I don't feel like trying to explain all the details because I am just not in the mood for it right now (its Friday and I am watching some recorded TV-Shows).
So, I will leave you with your conviction but I will continue to use integer based EA's because I have had the benefit of using and testing both types in "action" and know which type is more efficient.
EDIT: Here is a hint - Compare example 1 to example 2:
- Convert to "int", followed by 100 integer operations, and convert back to "double".
- Read "double", followed by 100 floating point operations, and write it back.
Do you see the difference now to your continuous conversion test?
Yes, I do have evidence to the contrary and I have tried to steer you in that direction, by stating that your test is doing continuous conversions, when that is not the case of how it is implemented. Sometimes a real-life implementation can be very different to a "lab" simulation.
It is not a question of clashing personalities. It is just that I don't feel like trying to explain all the details because I am just not in the mood for it right now (its Friday and I am watching some recorded TV-Shows).
So, I will leave you with your conviction but I will continue to use integer based EA's because I have had the benefit of using and testing both types in "action" and know which type is more efficient.
No, I think you're missing my point that with a proper double comparison you don't have to cast. That IS the optimization. Other than that there are no tricks and each bit of code is fully executed on each iteration. It's just plain-faster to avoid casting and go for a universal double comparison.
Regarding your first point, it's only evidence when you publish a test that can be replicated. After-all, that is the scientific method.
I'm trying to skew the test results in favor of your method based on your description that int comparison is much faster. I created a CompareDouble function which returns 0=equal, 1=greater, -1=less which has more than one comparison. This is where the <int> version should shine since there are more comparisons, but it's negligibly faster than Method #1 and still slower than Method #2.
1: 2519 ms for 100000000 iterations 2: 1414 ms for 100000000 iterations 3: 2305 ms for 100000000 iterations
void OnStart() { ulong iterations = (ulong)pow(10,8); MqlTick tick; SymbolInfoTick(_Symbol,tick); double price[3]; price[0] = tick.bid; price[1] = tick.ask; price[2] = tick.ask + 1.0*_Point; srand(_RandomSeed); /////////////////////////////////////////////////////////////////// long result=0; ulong m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) result+= CompareDoubles1(price[rand()%3],price[rand()%3]); m=GetMicrosecondCount()-m; m/=1000; printf("1: %d ms for %d iterations",m,iterations); printf("result = %d",result); /////////////////////////////////////////////////////////////////// result=0; m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) result+= CompareDoubles2(price[rand()%3],price[rand()%3]); m=GetMicrosecondCount()-m; m/=1000; printf("2: %d ms for %d iterations",m,iterations); printf("result = %d",result); /////////////////////////////////////////////////////////////////// result=0; m = GetMicrosecondCount(); for(ulong i=0;i<iterations;i++) result+= CompareDoubles3(price[rand()%3],price[rand()%3]); m=GetMicrosecondCount()-m; m/=1000; printf("3: %d ms for %d iterations",m,iterations); printf("result = %d",result); } //+------------------------------------------------------------------+ int CompareDoubles1(const double num1,const double num2) { double res = NormalizeDouble(num1-num2,_Digits); if(res==0.0) return 0; if(res > 0.0) return 1; return -1; } int CompareDoubles2(const double num1,const double num2) { double res = num1-num2; if(fabs(res) < _Point / 10.0 * 5) return 0; if(res > 0.0) return 1; return -1; } int CompareDoubles3(const double num1,const double num2) { int res = ((int)round(num1 / _Point) - (int)round(num2 / _Point)); if(res==0) return 0; if(res > 0) return 1; return -1; }
- www.metatrader5.com
No, I think you're missing my point that with a proper double comparison you don't have to cast. That IS the optimization. Other than that there are no tricks and each bit of code is fully executed on each iteration. It's just plain-faster to avoid casting and go for a universal double comparison.
Regarding your first point, it's only evidence when you publish a test that can be replicated. After-all, that is the scientific method.
I'm trying to skew the test results in favor of your method based on your description that int comparison is much faster. I created a CompareDouble function which returns 0=equal, 1=greater, -1=less which has more than one comparison. This is where the <int> version should shine since there are more comparisons, but it's negligibly faster than Method #1 and still slower than Method #2.
Your tests are still using continuous conversions/casting. I have already stated, that it is not the case and even gave your you an example in the hint!
Do as you wish! I will not be posting any further on the subject! Have a good evening!
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use