Features of the mql5 language, subtleties and tricks - page 302

 
Aleksandr Slavskii #:
In fact, in all others with double numbers.


   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
you don't have to compare in robots
 

And why did you suddenly start discussing the comparison of double variables if the original question was

Forum on trading, automated trading systems and testing trading strategies.

Peculiarities of mql5 language, subtleties and techniques of work

Andrei Iakovlev, 2025.05.19 14:56

Some variable has a value equal to -1.

Which check will be executed faster to check this value: < 0 or == -1?


And further suggested

Better how? As in that anecdote... "better than ...".

 
Putnik #:

It is better to replace a==b with (a-b)==0 , it is better.

I think this is only meaningful (and may be more error tolerant if either a or b is a calculated value) for comapring doubles a and b.

For doubles:

MathAbs(a-b)<DBL_EPSILON

or

NormalizeDouble(a-b, 8)==0

For integers:

a==b is better (and more clear)

 

I use integers for prices. The minimum step in the price value is one Point. It is a constant, has a fixed size, i.e. a discrete value. But discreteness is also a property of _ integers. Therefore, by converting prices to an integer (int), you can perform calculations without loss of precision.

For example:

EUR/USD quote = 1.17191, convert to int, we get 117191;

TakeProfit=0.00500, in int we get 500;

ТР=117191+500=117691.

I use price arrays in my programme. I easily compare them if(a==b), NormalizeDouble() normalisation is not required. I use double numbers only when accessing the server, when receiving quotes, and when sending OrderSend().

In the Print() function, I use a record like ".117191" or ".085338". It is quite clear.

 
Putnik #:

I use integers for prices. The minimum step in the price value is one Point. It is a constant, has a fixed size, i.e. a discrete value. But discreteness is also a property of _ integers. Therefore, by converting prices to an integer (int), you can perform calculations without loss of precision.

For example:

EUR/USD quote = 1.17191, convert to int, we get 117191;

TakeProfit=0.00500, in int we get 500;

ТР=117191+500=117691.

I use price arrays in my programme. I easily compare them if(a==b), NormalizeDouble() normalisation is not required. I use double numbers only when accessing the server, when receiving quotes, and when sending OrderSend().

In the Print() function, I use a record like ".117191" or ".085338". It is quite clear.

What you do is called fixed-point arithmetic, and you have to take care of the scale factor (e.g. scale = 100 000 for 5-digits prices) throughout all calculations.

However, it is not the most efficient way on MT5 platform, because originally the prices and lots are recieved as "double" floating-point.

 

BTW, here is a more precise way to compare doubles a & b (i.e., calculated values, not constants) for equality:

MathAbs(a - b) < DBL_EPSILON * MathMax(MathAbs(a), MathAbs(b))

As this comparison can handle very big or very small numbers gracefully.

Edit:

For other forms of comparisons (>, <, etc) please check my library here: https://www.mql5.com/en/code/20822

Math Utils
Math Utils
  • www.mql5.com
Handy functions for comparison, rounding, formatting and debugging of doubles (prices, lots and money).
 
amrali #:

BTW, here's a more accurate way to compare the dual numbers a and b (i.e. calculated values, not constants) for equality:

Since this comparison can handle very large or very small numbers gracefully.


Yes, this method is the most correct.
 
Is d1 or d2 normalised?
void OnStart()
{
  double d1 = (string)"1.0054400000000001";
  double d2 = (string)"1.00544";
  
  Print(d1 == NormalizeDouble(d1, 8)); // true
  Print(d2 == NormalizeDouble(d2, 8)); // false
  Print(d1 == d2 + DBL_EPSILON);       // true
}
 
fxsaber #:
Is d1 or d2 normalised?

A minor bug that I had prevoiusly reported in NormalizeDouble() function where small round-off errors (= 1 ULP; unit in the last place) is frequently introduced in the function output.

If you want "very" precise rounding to the least significant bit (ULP), pleasae use my library function Round(num, digits) from "math_utils.mqh".

#include "math_utils.mqh"  // https://www.mql5.com/en/code/20822

void OnStart()
{
  double d1 = (string)"1.0054400000000001";
  double d2 = (string)"1.00544";

  Print(d1);                       // 1.0054400000000001
  Print(d2);                       // 1.00544

  // round-off errors by NormalizeDouble()
  Print(NormalizeDouble(d1, 8));   // 1.0054400000000001
  Print(NormalizeDouble(d2, 8));   // 1.0054400000000001

  // precise rounding (math_utils.mqh)
  Print(Round(d1, 8));             // 1.00544
  Print(Round(d2, 8));             // 1.00544
}

Edit:

A "precise" Round function can be implemented as:

//+------------------------------------------------------------------+
//| Round to a specified number of decimal digits (no-round off).    |
//+------------------------------------------------------------------+
double Round(const double value, const int digits)
  {
   double pwr10 = MathPow(10, digits);
   return MathRound((value * pwr10) * (1 + DBL_EPSILON)) / pwr10;
  }

math_utils.mqh contains a rather optimized version of Round() for speed.

BTW, in older versions of MT5, Print() function had errors when printing doubles, so it was very hard to detect where exactly the fp roundoff errors occur. 

The Print() function is now assumed to be correct in MT5 (I did not test Print() on MT4).

Edit2:

Another issue in your code is the exact comparison of doubles (calculated values)

  Print(d2 == NormalizeDouble(d2, 8)); // false

You should use loose (more tolerant) comparison using the absoulte difference as before

  double a = (string)"1.00544";
  double b = NormalizeDouble(a, 8);

  // check if a, b are almost equal
  Print( MathAbs(a - b) < DBL_EPSILON * MathMax(MathAbs(a), MathAbs(b)) );  // true
MT5/mql5 reported and confirmed bugs. - How to fix a problem with the format of StringToDouble
MT5/mql5 reported and confirmed bugs. - How to fix a problem with the format of StringToDouble
  • 2022.02.13
  • Alain Verleyen
  • www.mql5.com
Why are you thinking the problem is normalizedouble (it could be but we have no idea about it) when stringtodouble give 1. To check if conversion string -> double is correct, we can examine it here  https://baseconvert
 
amrali #:

If you need "very" precise rounding to the least significant bit (ULP), please use my library function Round(num, digits) from"math_utils.mqh".

I need a fast decompression of the compressed MqlTick. This option is unfortunately slow.