Download MetaTrader 5

Working with Doubles in MQL4

4 November 2009, 16:06
MetaQuotes Software Corp.
5
9 086

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 (5)
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.
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

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.

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?

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.

FANN2MQL Neural Network Tutorial FANN2MQL Neural Network Tutorial

This article has been made to show you how to use neural networks, via FANN2MQL, using an easy example: teaching a simple pattern to the neuralnetwork, and testing it to see if it can recognize patterns it has never seen.