Русский 中文 Español Deutsch 日本語 Português
Working with Doubles in MQL4

Working with Doubles in MQL4

MetaTrader 4Examples | 4 November 2009, 16:06
18 650 6
MetaQuotes
MetaQuotes

Introduction

The MQL programming opens new opportunities for the automated trading, many people all over the world already have appreciated it.

When we are writing an Expert Advisor for trading, we must be sure that it will work correctly.

Many newbies often have some questions when the results of some mathematical calculations differ from those expected. The program is compiled and can work, but not as it should. They are checking the code again and again, finding the new "mistakes" in language, in implementation, in functions, etc.

In the most of cases, the careful analysis shows that the language and compiler work correctly, but code has a small error, and it can take a long time to find and correct it.

In this note we will consider a typical programming errors, that occurs while working with double numbers in MQL4 programs.


1. Verifying the numerical values

To verify the calculation results and to debug you can use the function DoubleToStrMorePrecision(double number, int precision) of standard library stdlib.mq4, which allows you to control the numerical values of double numbers to the specified precision.

It will allow to save time in searching for possible errors.

An example:

#include <stdlib.mqh>
int start()
  {
   double a=2.0/3;
   Alert("Standard output:",a,", 8 digits precision:",DoubleToStr(a,8),", 15 digits precision:", DoubleToStrMorePrecision(a,15));
   return(0);
  }  

Result:

Standard output:0.6667, 8 digits precision:0.66666667, 15 digits precision:0.666666666666667

In some cases to show the numerical values of double numbers (for example, in Print, Alert, Comment) it is better to use the functions DoubleToStr and DoubleToStrMorePrecision (from stdlib.mq4) to display more precise values, instead of the standard 4 digits output precision.

For example:

#include <stdlib.mqh>
int start()
  {
   double a=2.0/100000;
   Alert("Standard output=",a,", More precise output=",DoubleToStrMorePrecision(a,15));
   return(0);
  }

returns: "Standard output=0, More precise output=0.000020000000000".


2. Accuracy of the decimal digits precision

Because of the double precision floating-point format there is a limited accuracy of their storage.

For example, if we assume that we have an unlimited precision, as in theory, for any double numbers A and B, the following expressions are always valid:

(A/B)*(B)=A,

A-(A/B)*B=0,

(A/B)*(B/A)=1 etc.

The accuracy of the decimal digits storage in computer is dependent on the fraction size and limited by 52 bits. To illustrate this fact, let's consider the following example.

In the first cycle (i) we are calculating the factorial of 23 (product of the integers from 2 to 23), and the result is: 23!=25852016738884976640000. The result is stored in the variable a of double type.

In the next cycle (j), we are dividing the resulted value a by all of the integers from 23 to 2. It seems that finally we can expect that a=1.

#include <stdlib.mqh>
int start()
  {
   int maxfact=23;
   double a=1;
   for (int i=2; i<=maxfact; i++) { a=a*i; }
   for (int j=maxfact; j>=2; j--) { a=a/j; }
   Alert(" a=",DoubleToStrMorePrecision(a,16));
   return(0);
  }

Instead it we have:

a=1.0000000000000002

As we see we have got an inaccurancy in 16th digit.

If we increase the calculation to 35!, we will get a=0.9999999999999998.

The MQL language has a function NormalizeDouble, which allows to round the double number to the specified precision.

The constants of the double type are stored in memory in a similar way as double variables, therefore it is necessary to take into account the limit of 15 significant digits in their definition.

But do not confuse the accuracy of the decimal digits precision with the calculation precision for the double numbers.

The range of possible values for the double numbers is much wider: from -1.7*e-308 to 1.7*e308.

Let's try to estimate the smallest exponent of the double number.
int start()
  {
  double R=1;
  int minpwr=0;
  while (R>0) {R=R/10; minpwr--;}
  Alert(minpwr);
  return(0);
  }

The program will print -324, but we have to take into account the decimal digits in fraction (+15) and we will get an approximate value for the exponent of the smallest double number.


3. Function NormalizeDouble

The function NormalizeDouble (double value, int digits) rounds the floating point value to the given precision. Returns normalized value of the double type.

For example:

int start()
  {
   double a=3.141592653589;
   Alert("a=",DoubleToStr(NormalizeDouble(a,5),8));
   return(0);
  }

the result is:

a=3.14159000

Note that in trading operations it is impossible to use the unnormalized prices, whose accuracy exceeds at least by one digit demanded by a trading server.
The StopLoss, TakeProfit and Price values for the pending orders should be normalized with the accuracy, which value is stored in predetermined variable Digits.


4. Check for equality of two double numbers

It is recommended to compare two double numbers using the function CompareDoubles(double number1,double number2) of the stdlib.mq4 library, which looks as:

//+------------------------------------------------------------------+
//| correct comparison of 2 doubles                                  |
//+------------------------------------------------------------------+
bool CompareDoubles(double number1,double number2)
  {
   if(NormalizeDouble(number1-number2,8)==0) return(true);
   else return(false);
  }

This function compares number1 and number2 of double type with an accuracy of up to 8 decimal digits.

The example:

#include <stdlib.mqh>
int start()
  {double a=0.123456781;
   double b=0.123456782; 
   if (CompareDoubles(a,b)) {Alert("They are equal");}
   else {Alert("They are different");}
  }

will output:

They are equal

because the are differ only in the 9th digit.

If necessary, you can write your own compare function (with the desired accuracy) in a similar way.


5. Dividing integers

It is necessary to remember, that if we are dividing two integers, we will get an integer number as result.

That's why the code:

int start()
  {
   Alert(70/100);
   return(0);
  }

will output 0, because 70 and 100 are integers.

As well as C/C++ language, in MQL the result of division of two integers will be an integer, in this case it is 0.

However, if the numerator or denominator is double (i.e. has a fractional part), the result will be a double. Therefore Alert (70/100.0); will output the right value 0.7.

For example:
int start()
  { double a=1/3;
    double b=1.0/3;
   Alert("a=",a,", b=",b);
   return(0);
  }

will output "a=0, b=0.3333"

6. Typecasting for integer and double numbers

Let's consider the code:
double xbaseBid=1.2972;
double xBid=1.2973;
double xPoint=0.0001;
int i = 100 + (xBid - xbaseBid)/xPoint;
Alert(i);

We will get 100, but it seems that it should be 101, because of the evident equity: 0.0001/0.0001=1

The same example in C/C++:
double baseBid=1.2972,Bid=1.2973,Point=0.0001;
int i = 100 + (Bid - baseBid)/Point;
printf("%d\n",i);

also gives 100.

To determine the reason of this fact, let's consider the code:

double a=0.99999999999999;
int i = 100 + a;
Alert(i);

which gives i=100.

However, if we perform some accuracy improvement for a:

double a=0.999999999999999;
int i = 100 + a;
Alert(i);

then we will get 101.

The reason of this fact is the use of integer and double numbers, complicated by low accuracy values.

Therefore, using the operations of such type it is recommended to perform the rounding of the similar expressions using the function MathRound(double value)
which returns the rounded double value to the nearest integer:

double baseBid=1.2972;
double xBid=1.2973;
double xPoint=0.0001;
int i = 100 + MathRound((xBid - baseBid)/xPoint);
Alert(i);

In such case we will get the right value 101.

There is a common error in using the function OrderModify, especially for the Trailing Stop programming. The function OrderModify returns an error № 1: ERR_NO_RESULT in the case if the new values for the orders are the same as already defined. So it is necessary to perform a careful check for the equality (use NormalizeDouble) and a careful points size calculations.

Remember that the client terminal allows to change the order parameters only if the new values differ from already defined at least in 1 point.


7. Features of function MathMod


In MQL the MathMod (double v1, double v2) function completely corresponds to the function fmod (double v1, double v2) of MSVC6, because of the direct call for the fmod function from C Runtime Library has used.

In some cases the function fmod of MSVC6 (and also MathMod) gives wrong results.

If you are using the function MathMod in your programs, please replace it to the following function:
double MathModCorrect(double a, double b)
{ int tmpres=a/b;
return(a-tmpres*b);
}

Note that this remark is for MQL4 only, MQL5 calculates this function by its mathematical definition.


Conclusion

Note that the above list is not exhaustive, if you have found something new, which hasn't been described here, look at the comments.

If you haven't found a solution, describe it in the comments, add your code and MQL community professionals will help you.


Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/1561

Last comments | Go to discussion (6)
forexrun
forexrun | 10 Apr 2016 at 14:14

I am new to mql programming and the issue 1 discussed here( Verifying the numerical numbers) was a show stopper for me. Lucky I saw this within 6 months after starting mql programming. This I hope is sorted in mql5 and never heard of in any other high level languages that I am familiar with.

Alain Verleyen
Alain Verleyen | 10 Apr 2016 at 19:52
forexrun:

I am new to mql programming and the issue 1 discussed here( Verifying the numerical numbers) was a show stopper for me. Lucky I saw this within 6 months after starting mql programming. This I hope is sorted in mql5 and never heard of in any other high level languages that I am familiar with.

The same is true in all programming languages which are using double data type.
abcmql4
abcmql4 | 29 Oct 2016 at 20:28
Hello and thanks for all this information,

I am a big problem now, since it had never required

I need to work and pass the result in a "double" a "string", but this number is 106 decimal, need them all, as I do?

put the link of what I put in the forum, where I explain what I need, thank you very much aid they can offer me

 https://www.mql5.com/en/forum/160483 


I hope you can help me. 

Mladen Rakic
Mladen Rakic | 29 Oct 2016 at 22:11
abcmql4:
Hello and thanks for all this information,

I am a big problem now, since it had never required

I need to work and pass the result in a "double" a "string", but this number is 106 decimal, need them all, as I do?

put the link of what I put in the forum, where I explain what I need, thank you very much aid they can offer me

 https://www.mql5.com/en/forum/160483 


I hope you can help me. 

You can not do that that way (double has 15 significant digits)

If you need only the string you can declare it as a string right away and then you will not lose information - like this :

   string SE = "result  = 1.23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678";      

But any conversion to and from a double will leave you with only 15 accurate digits result

You can also check this : https://en.wikipedia.org/wiki/List_of_arbitrary-precision_arithmetic_software  for arbitrary precision arithmetic libraries and software capable of doing it

Antonio Simon Del Vecchio
Antonio Simon Del Vecchio | 28 Apr 2020 at 21:41

The function double MathModCorrect(double a, double b) generates an error if the "a" and "b" parameters are below one (1). To be able to work with numbers below one (1) you can use the following function:

double MathModCorrect(double a, double b)
  {
   int i = 1;
   double El_Menor;
   if(a > b)
      El_Menor = b;
   else
      El_Menor = a;
   while(!El_Menor == int(El_Menor))
     {
      a = a * i;
      b = b * i;
      El_Menor = El_Menor * i;
      i = i * 10;
     }
   int tmpres = a / b;
   return((a - tmpres * b) / i);
  }
Trader's Kit: Decorating Indicators Trader's Kit: Decorating Indicators
In this article you will find main tasks when decorating indicators, their solution and automation.
Interaction between MеtaTrader 4 and MATLAB Engine (Virtual MATLAB Machine) Interaction between MеtaTrader 4 and MATLAB Engine (Virtual MATLAB Machine)
The article contains considerations regarding creation of a DLL library - wrapper that will enable the interaction of MetaTrader 4 and the MATLAB mathematical desktop package. It describes "pitfalls" and ways to overcome them. The article is intended for prepared C/C++ programmers that use the Borland C++ Builder 6 compiler.
Auto-Generated Documentation for MQL5 Code Auto-Generated Documentation for MQL5 Code
Most Java coders will be familiar with the auto-generated documentation that can be created with JavaDocs. The idea is to add comments into the code in a semi-structured way that can then be extracted into an easy to navigate help file. The C++ world also has a number of documentation auto-generators, with Microsoft's SandCastle and Doxygen being two leaders. The article describes the use of Doxygen to create HTML help file from structured comments in MQL5 code. The experiment worked very well and I believe the help documentation that Doxygen produces from MQL5 code will add a great deal of value.
Superposition and Interference of Financial Securities Superposition and Interference of Financial Securities
The more factors influence the behavior of a currency pair, the more difficult it is to evaluate its behavior and make up future forecasts. Therefore, if we managed to extract components of a currency pair, values of a national currency that change with the time, we could considerably delimit the freedom of national currency movement as compared to the currency pair with this currency, as well as the number of factors influencing its behavior. As a result we would increase the accuracy of its behavior estimation and future forecasting. How can we do that?