Double Precision Math

 

Hi there,

From what I have read on this forum regarding Double Precision Math operations the concensus seems to be that one must use the NormalizeDouble function to produce expected results when using double precision numbers.

However, I am still experiencing some strange problems when using double precision math in MQL.

Here is a simple script that illustrates one such problem

int start()
{

double price = 1.5235;
double price_normalized = NormalizeDouble(price, 4);
int i_price, i_price_normalized;

Print("price="+DoubleToStr(price, 8)+" price_normalized="+DoubleToStr(price_normalized, 8));

if (price == price_normalized) {
Print("price == price_normalized OK");
}
else {
Print("price != price_normalized NOT OK");
}

i_price = price / 0.0005;
i_price_normalized = price_normalized / 0.0005;

Print("i_price="+i_price+" i_price_normalized="+i_price_normalized);


return(0);
}

We have a double variable called price which is initialized with a value of 1.5235.

We have another double variable called price_normalized which is equal to NormalizeDouble(price, 4).

I would expect price and price_normalized to be equal to one another, but they are not.

Furthermore, if I divide price by 0.0005 and assign the result to an integer called i_price I get the correct result of 3047.

If I divide price_normalized by 0.0005 and assign the result to an integer called i_price_normalized I get an incorrect result of 3046.

Even though price_normalized has been normalized to 4 digits it does not seem to behave in the way that I would expect.

Can anyone explain why the above math doesn't work as expected even though I am using NormalizeDouble?

Regards,

Laurence.

 

fractional numbers cannot ALWAYS be represented exactly in binary. For that reason, comparing doubles for equality sometimes fails.

Convert your doubles to integers for comparison if you must compare for equality.

There is a huge volume of information on the topic.

http://www.google.com/search?sourceid=navclient&ie=UTF-8&rlz=1T4GGLJ_enUS256US256&q=floating+point+compare

 
phy wrote >>

fractional numbers cannot ALWAYS be represented exactly in binary. For that reason, comparing doubles for equality sometimes fails.

Convert your doubles to integers for comparison if you must compare for equality.

There is a huge volume of information on the topic.

http://www.google.com/search?sourceid=navclient&ie=UTF-8&rlz=1T4GGLJ_enUS256US256&q=floating+point+compare

Sure, I realize that. I am fully aware that fractional numbers cannot always be represented precisely in binary.

But isn't that the reason for using NormalizeDouble in math operations?

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

Referring to my example again I have a double called price which is initialized with the value 1.5235. This is a fractional number to 4 decimal places.

If I use price_normalized = NormalizeDouble(price, 4) this should produce a fractional number to 4 decimal places.

Since price is already stored to 4 decimal places one would assume that in this case price and price_normalized would in this case be the same, buy they are not.

Taking your suggestion I modified the code to convert price and price_normalized to integers by multiplying them by 10000 and assigning them to integers i_price_10000 and i_price_normalized_10000.

int start()
{

double price = 1.5235;
double price_normalized = NormalizeDouble(price, 4);
int i_price_10000, i_price_normalized_10000;

Print("price="+DoubleToStr(price, 8)+" price_normalized="+DoubleToStr(price_normalized, 8));

i_price_10000 = price * 10000;
i_price_normalized_10000 = price_normalized * 10000;

Print("i_price_10000="+i_price_10000+" i_price_normalized_10000="+i_price_normalized_10000);

return(0);
}

Doing this i_price_10000 equals 15235 which is correct and i_price_normalized_10000 equals 15234 which is incorrect. So converting the values to integers doesn't work in this example.

Since the only difference between price and price_normalized is the fact that price_normalized is the result of NormalizeDouble(price, 4)

I can only assume that there is some issue with NormalizeDouble since price and price_normalized are obviously different.

In my example if price has an initialised valie of 1.5235 and is already specified to 4 decimal places then why would NormalizeDouble(price, 4) return a value different to price as seems to be the case?

If the value 1.5235 could not be represented precisely in binary then arithmetic on price would produce incorrect results. Since arithmetic on price appears to produces correct results in my example then I can only assume that the value 1.5235 stored in price is represented precisely in binary.

Regards,

Laurence.

 

I dunno.

 
phy wrote >>

I dunno.

Can anyone else shed any light on this NormalizeDouble problem?

 

http://www.cs.nmsu.edu/~pfeiffer/classes/473/notes/fp.html

Interesting page on how to convert decimal to floating point.

 
phy wrote >>

http://www.cs.nmsu.edu/~pfeiffer/classes/473/notes/fp.html

Interesting page on how to convert decimal to floating point.

Since NormalizeDouble does work as expected I wrote my own rounding function RoundDouble that does. I would appreciate feedback if anyone sees anything wrong with the code.

//+------------------------------------------------------------------+
//|                                                    normalize.mq4 |
//|                              Copyright © 2008, Laurence Hookway. |
//|                                        https://www.metaquotes.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2008, Laurence Hookway."
#property link      "https://www.metaquotes.net"

//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+


double RoundDouble(double price, int precision) {

  int integer;
  double factor;
  static double rounding_factor[] = {1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0 };   

  if (precision < 0) {
    precision = Digits;
  }

  if (precision >= 10) {  
    factor = MathPow(10, precision);
  }
  else {
    factor = rounding_factor[precision];
  }
  
  if (price >= 0) {
    integer = (price * factor) + 0.5;
  }
  else {
    integer = (price * factor) - 0.5;
  }
  
  return (integer/factor);
}
int start()
  {
  
   double price, price_fractional, price_normalized, price_rounded;
   int    i_price, i_price_normalized, i_price_rounded;

   price = 1.5235;
   price_fractional = price + 0.00004999;
   price_normalized = NormalizeDouble(price_fractional, 4);
   price_rounded    = RoundDouble(price_fractional, 4);

   Print("price="+DoubleToStr(price, 8)+" price_fractional="+DoubleToStr(price_fractional, 8)+" price_normalized="+DoubleToStr(price_normalized, 8)+" price_rounded="+DoubleToStr(price_normalized, 8));   
   
   if (price == price_normalized) {
     Print("price == price_normalized OK");
   }
   else {
     Print("price != price_normalized NOT OK");
   }

   if (price == price_rounded) {
     Print("price == price_rounded OK");
   }
   else {
     Print("price != price_rounded NOT OK");
   }

   i_price = price / 0.0005;
   i_price_normalized = price_normalized / 0.0005;
   i_price_rounded = price_rounded / 0.0005;

   Print("i_price="+i_price+" i_price_normalized="+i_price_normalized+" i_price_rounded="+i_price_rounded);   
   
   return(0);
  }
//+------------------------------------------------------------------+

Regards,

Laurence.

Files:
normalize.mq4  3 kb
 
lhookway mate, thanks for your input, i was about to throw my computer out of the window because of the frustration this has caused me :)
 

Better to use the difference between two numbers for decision-making, like for example

if ( MathAbs (number#1 - number#2 ) <= some_lilliputian_number ) ...........


where

some_lilliputian_number = 0.001, 0.002...,0.00005 ... as reasonable small as you want to achieve results in our lifetime

 
skaboy:

Better to use the difference between two numbers for decision-making, like for example

if ( MathAbs (number#1 - number#2 ) <= some_lilliputian_number ) ...........


where

some_lilliputian_number = 0.001, 0.002...,0.00005 ... as reasonable small as you want to achieve results in our lifetime

Why are you replying to a 4 year old post ? this subject has been discussed much more recently . . .

Can price != price ?