Buggy MathFloor() ?

 

Simple code but might have big impact on your trading results.

MathFloor() delivers wrong result.

#property strict
void OnStart()
  {
   //double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double price = 1.24579; // <<<< could ask / bid price. This specific value caught my attention.

   double ticksize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double normalized = price / ticksize; // part of incomplete code, not shown here.
   double floored = MathFloor(normalized);
   bool equal = (normalized == floored);
   double delta = normalized - floored;
   
   PrintFormat("*** MQL5 Build: %d", __MQL5BUILD__);
   
   PrintFormat("*** %s: Ticksize: %.5f ::: Price: %.5f",
               _Symbol,
               ticksize,
               price);
   PrintFormat("*** Price: %.5f / %.5f => %.5f (%.5f ; %.5f)",
               price,
               ticksize,
               normalized,
               floored,
               MathFloor(normalized));
   PrintFormat("*** %.5f == %.5f => %s [delta= %.5f]",
               normalized,
               floored,
               equal?"EQUAL":"DIFFER",
               delta);
  }

Result:
Buggy MathFloor()

Files:
buggy.mq5  2 kb
 
Update:

Printing all double values with more precisions we can see that division using double values in MQL delivers wrong result, that leads MathFloor() to deliver wrong result.
   PrintFormat("*** MQL5 Build: %d", __MQL5BUILD__);
   
   PrintFormat("*** %s: Ticksize: %.16f ::: Price: %.16f",
               _Symbol,
               ticksize,
               price);
   PrintFormat("*** Price: %.16f / %.16f => %.16f (%.16f ; %.16f)",
               price,
               ticksize,
               normalized,
               floored,
               MathFloor(normalized));
   PrintFormat("*** %.16f == %.16f => %s [delta= %.16f]",
               normalized,
               floored,
               equal?"EQUAL":"DIFFER",
               delta);

Result:
Buggy doubles division

Further update as "solution" (or workaround ?):
Later I found following article (it's for MT4 but I guess it applies for MT5 also).
As a note to myself too, NormalizeDouble() is our friend.

IMHO, working with doubles seems too buggy and too dangerous to take it as granted in MQL, thus always use NormalizeDouble() and be aware of the in-precision built-in.

https://www.mql5.com/en/articles/1561

Working with Doubles in MQL4
Working with Doubles in MQL4
  • www.mql5.com
In this note we will consider a typical programming errors, that occurs while working with double numbers in MQL4 programs.
 

There are no bugs in MathFloor, MQL5 or math calculations.

Accuracy of the float/double numbers is the common issue for all computer languages/processors.

Root of the mistake:

double normalized = price / ticksize;    // it's NOT NORMALIZED!
 
MetaQuotes #:

There are no bugs in MathFloor, MQL5 or math calculations.

Accuracy of the float/double numbers is the common issue for all computer languages/processors.

Root of the mistake:

Agree, except nowadays one would expect that "simple" division with low precision doubles, should be correctly calculated.

1.24579 / 0.00001 would be correctly calculated by any cheap calculator nowadays.

Fpr (20.0 / 7.0) * 7.0, I have seen calculators that failed to calculate correctly ;)

Anyway, thanks for the confirmation.

 
  1. Floating point has an infinite number of decimals, it's you were not understanding floating point and that some numbers can't be represented exactly. (like 1/10.)
              Double-precision floating-point format - Wikipedia, the free encyclopedia

    See also The == operand. - MQL4 programming forum (2013)

  2. double normalized = price / ticksize;    // it's NOT NORMALIZED!

    The result is 124578.9999999999854481, then you floor from that. You should have rounded to the nearest int.

 
That is not a bug with MathFloor().

It is a actually a floating-point round-off error (i.e., loss of precision) due to the division of price / ticksize. This is an inherent feature (or bug !) of ieee-754 floating-point format that affects all programming languages. 

To be able to floor correctly as expected use this function
Floor(const double value, const double step);

From my "Math Utils" library: https://www.mql5.com/en/code/20822


#include <math_utils.mqh>

void OnStart(void)
  {
   double price = 1.24579;
   double ticksize = 0.00001;

   Print( MathFloor(price / ticksize) * ticksize );
   Print( Floor(price, ticksize)                 );  // https://www.mql5.com/en/code/20822
  }

// 1.24578
// 1.24579


Edit:

I suggest that not using PrintFormat() for debugging doubles, as "%.f" approximates the numbers.  Just use Print(), it displays the exact numbers.
Math Utils
Math Utils
  • www.mql5.com
Handy functions for comparison, rounding, formatting and debugging of doubles (prices, lots and money).
Reason: