Imprecise double number calculation

 

Hello community,

came across a strange problem: It seems that calculations of double numbers can get unprecise.

The script below should demonstrate this. After a couple of runs, 2 double numbers which should be exactly the same

are sooner or later not same.

void OnStart()
{
   double NetPosition=0.0;
   double MinVolume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);

   for(int i=1;i < 1000;++i)
   {
      NetPosition += MinVolume;
      double MultipleMinVol=(double) i * MinVolume;
      if(NetPosition !=MultipleMinVol)
      {
         PrintFormat("NetPos=%.15f != MinVol x %d=%.15f",NetPosition,i,MultipleMinVol);
      }
   }
}

Effectively, i just add the MinVolume for symbol (0.01 EURUSD tested on 15MIN) to both: NetPosition and MultipleMinVol. So both numbers should hold exact same value but they start to differ after a couple of loops (in my tests 6 loops). This should not be case. How to make sure MetaTrader calculates correctly? Thank you.

.

 

Before you post, please run a few searches. This has been discussed many times.

Forum on trading, automated trading systems and testing trading strategies

Implicit or explicit decimals

Fernando Carreiro, 2023.08.13 22:11

Answering questions as one — all the "double" numbers are floating point and have no fixed number of decimal digits.

You will have to learn this by following the links and researching how binary floating point numbers work.

If you don't learn this, then you will always be handicapped in your coding.

Forum on trading, automated trading systems and testing trading strategies

MathRound fails for one particular number

Fernando Carreiro, 2018.01.01 22:08

He means that the value "0.69" cannot be exactly represented given the way a floating point number works (based on binary and not decimal representation) - hence, why the value gets represented as "0.68999999..." (see below).

You can never really "normalize" it and it is the main reason why both @whroeder1 and myself contest the use of the NormalizeDouble() function. It should in the very least be renamed to something like "RoundDigits()" because it is more of a "rounding" function and does not "normalize" in any way or fashion.


https://www.h-schmidt.net/FloatConverter/IEEE754.html

EDIT: Please note that the above images are for examples of representation in the 4-byte "float", and not the 8-byte "double" which offers more precision but still cannot represent the value "0.69" exactly due to the "binary" nature of the format.

EDIT2: For future readers that have difficulty understanding (or accepting) this concept, of decimal values not having an exact representation with a "float" or a "double", here is an article worth reading:

Why 0.1 Does Not Exist In Floating-Point

Many new programmers become aware of binary floating-point after seeing their programs give odd results: “Why does my program print 0.10000000000000001 when I enter 0.1?”; “Why does 0.3 + 0.6 = 0.89999999999999991?”; “Why does 6 * 0.1 not equal 0.6?” Questions like these are asked every day, on online forums like stackoverflow.com.

The answer is that most decimals have infinite representations in binary. Take 0.1 for example. It’s one of the simplest decimals you can think of, and yet it looks so complicated in binary:


Decimal 0.1 In Binary ( To 1369 Places

The bits go on forever; no matter how many of those bits you store in a computer, you will never end up with the binary equivalent of decimal 0.1.

... Read the rest of the article at: http://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point/

 

Forum on trading, automated trading systems and testing trading strategies

why is 1.85 is round up to 1.9 and 1.95 is round down to 1.9 ?

Fernando Carreiro, 2023.09.27 21:27

Because the exact values 1.85 and 1.95 cannot be represented in double floating point precision, because it is binary and not decimal.

It will become:

  • 1.8500000000000000888178419700125232338905334472656250
  • 1.9499999999999999555910790149937383830547332763671875

So, from the above two values, can you see why one will round up and the other will round down to 1.9?

Indecently 1.9 is actually expressed as 1.899999999999999911182158029987476766109466552734375

For reference: https://www.exploringbinary.com/floating-point-converter/

Forum on trading, automated trading systems and testing trading strategies

why return many decimal ?? also after normalize double?

Fernando Carreiro, 2023.09.11 13:43

It is not an exclusive MQL problem. It is the same for ALL programming languages that use floating point.

You can see that by the links I provided and I quote from one of them ...

"The questions came from the context of a variety languages: Java, C++, C#, Python, Javascript, awk, Perl, Objective-C, and PHP. "

Forum on trading, automated trading systems and testing trading strategies

Do I have to NormalizeDouble Everything ?

Fernando Carreiro, 2023.07.05 06:20

As already explained, floating point numbers are represented in binary form, not in decimal form. It is impossible to limit the number of "decimal" digits internally.

All you can do is approximate it, which is what NormaliseDouble already does, but you can never get to an exact decimal place because it is binary, not decimal.

 
Exact comparison of doubles

Sometimes, when comparing two double numbers that assumed to be equal, but reached to via different calculations, the comparison goes wrong. Actually A can differ from B by in very little amount (at the 16th decimal place) due to binary rounding errors, so exact comparisons (==, !=, >, >=, <, <= operators) fail.

This is an exact comparison that is failing:
if(NetPosition !=MultipleMinVol) 
What happened in your calculations is a round-off error. It is a feature (or bug) of ieee-754 floating point numbers. 

Test this:
Print(0.1 + 0.2 == 0.3);   // gives false 
To get rid off these tiny errors, you have to use some form of arithmetic rounding to approximate results to some precision that it is enough to draw proper conclusions. (NormalizeDouble, round, ceil or floor, etc).


For more in-depth discussion on this issue, you can refer to this Wikipedia article https://en.wikipedia.org/wiki/Round-off_error
 
So accept it, general problem with floating point numbers. 
 
chinaski #: So accept it, general problem floating point numbers. 

You have to deal with it:

Avoid the exact comparison:

if(NetPosition !=MultipleMinVol) 

Use a loose comparison:

double epsilon = 1e-8;
if(MathAbs(NetPosition-MultipleMinVol)) > epsilon)

Set your epsilon (minimum tolerance) to the smallest difference of interest.

Remember, be very cautious when you use comparison operators with doubles. You can use them if you know what you are doing or take the other way and use loose comparisons which are generally safer.

 

Documentation on MQL5: Constants, Enumerations and Structures / Named Constants / Numerical Type Constants

DBL_EPSILON

Minimal value, which satisfies the condition:

1.0+DBL_EPSILON != 1.0 (for double type)

2.2204460492503131e-016

Documentation on MQL5: Constants, Enumerations and Structures / Named Constants / Numerical Type Constants
Documentation on MQL5: Constants, Enumerations and Structures / Named Constants / Numerical Type Constants
  • www.mql5.com
Numerical Type Constants - Named Constants - Constants, Enumerations and Structures - MQL5 Reference - Reference on algorithmic/automated trading language for MetaTrader 5
 
Fernando Carreiro #:

Documentation on MQL5: Constants, Enumerations and Structures / Named Constants / Numerical Type Constants

DBL_EPSILON

Minimal value, which satisfies the condition:

1.0+DBL_EPSILON != 1.0 (for double type)

2.2204460492503131e-016

The dbl_epsilon value itself is too tiny and it is very strict for using as a min tolerance, better to use multiples of epsilon (100x or more).

Epsilon 1e-8 is overall OK, unless you do calculations on numbers with 8 decimal places, decrease it to 1e-12.
 
amrali #: You have to deal with it: Avoid the exact comparison: Use a loose comparison: Set your epsilon (minimum tolerance) to the smallest difference of interest. Remember, be very cautious when you use comparison operators with doubles. You can use them if you know what you are doing or take the other way and use loose comparisons which are generally safer.

I meanwhile did, thank you for all support.

 

@amrali, a reminder to please use the CODE button (Alt-S) when inserting code. I have edited your posts accordingly.

Code button in editor

 
Dear Fernando, I am sorry for that coz I was posting from mobile phone, appreciating your great support for the forum.