Download MetaTrader 5

Working with Doubles in MQL4

4 November 2009, 16:06
MetaQuotes Software Corp.
3
5 488

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 Software Corp.
Original article: https://www.mql5.com/ru/articles/1561

Last comments | Go to discussion (3)
MQL4 Comments
MQL4 Comments | 5 Nov 2009 at 22:36

Great article! I was getting a lot of error code # 1's in my OrderModify calls for trailing stops. I kinda figured it was due to calling the function with no change in the parameters, but wasn't sure how to fix it. Using NormalizeDouble in my check for trailing stops took care of the problem.


Also was getting error code 4107 from my OrderSend function calls when using Strategy Tester. It seems Strategy Tester is using historical values at 5 decimal points, when I'm testing a currency pair with 4 decimal points. Using NormalizeDouble(Ask,Digits), or NormalizeDouble(Bid,Digits) in my OrderSend function took care of the error. Cool!

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.
Step on New Rails: Custom Indicators in MQL5 Step on New Rails: Custom Indicators in MQL5

I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider the indicators, their structure, drawing, types and their programming details, as compared to MQL4. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new.

Here Comes the New MetaTrader 5 and MQL5 Here Comes the New MetaTrader 5 and MQL5

This is just a brief review of MetaTrader 5. I can't describe all the system's new features for such a short time period - the testing started on 2009.09.09. This is a symbolical date, and I am sure it will be a lucky number. A few days have passed since I got the beta version of the MetaTrader 5 terminal and MQL5. I haven't managed to try all its features, but I am already impressed.

False trigger protection for Trading Robot False trigger protection for Trading Robot

Profitability of trading systems is defined not only by logic and precision of analyzing the financial instrument dynamics, but also by the quality of the performance algorithm of this logic. False trigger is typical for low quality performance of the main logic of a trading robot. Ways of solving the specified problem are considered in this article.

Using text files for storing input parameters of Expert Advisors, indicators and scripts Using text files for storing input parameters of Expert Advisors, indicators and scripts

The article describes the application of text files for storing dynamic objects, arrays and other variables used as properties of Expert Advisors, indicators and scripts. The files serve as a convenient addition to the functionality of standard tools offered by MQL languages.