Can't round a number "0.13"

 

I can't place an order because I get an error that there's too many digits in the quantity.

I really hope someone can help me with this, because I can't run my algo at all whilst I have this persistent problem.


For example, if I try      double thisQty=(double)"0.13"; 

I always get  0.130000000000000004

Here's what I've tried:

  double thisQty=0.130000000000000004; // This is the value I get to begin with.

   thisQty=NormalizeDouble(thisQty,2);

   string strQty=DoubleToString(thisQty,2);

   thisQty=(double)strQty;

   thisQty=round(thisQty*100)/100;

   thisQty=(MathFloor(thisQty * 100)) / 100;

   thisQty=MathRound(thisQty*100)/100;


 
Sandstorm2: I can't place an order because I get an error that there's too many digits in the quantity. I really hope someone can help me with this, because I can't run my algo at all whilst I have this persistent problem.

You have to check your broker's volume conditions ...

Forum on trading, automated trading systems and testing trading strategies

How to calculate lots using multiplier according to number of opened orders?

Fernando Carreiro, 2017.09.01 21:57

Don't use NormalizeDouble(). Here is some guidance (code is untested, just serves as example):

// Variables for Symbol Volume Conditions
double
   dblLotsMinimum = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MIN  ),
   dblLotsMaximum = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MAX  ),
   dblLotsStep    = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_STEP );
   
// Variables for Geometric Progression
double
   dblGeoRatio = 2.8,
   dblGeoInit  = dblLotsMinimum;
   
// Calculate Next Geometric Element
double
   dblGeoNext  = dblGeoInit * pow( dblGeoRatio, intOrderCount + 1 );
   
// Adjust Volume for allowable conditions
double
   dblLotsNext = fmin( dblLotsMaximum,                                     // Prevent too greater volume
                   fmax( dblLotsMinimum,                                   // Prevent too smaller volume
                     round( dblGeoNext / dblLotsStep ) * dblLotsStep ) );  // Align to Step value
 

I just figured out that I had two problems. So, even with the long number it still accepts the quantity for ordering, so long as I round it.

 
Sandstorm2:I just figured out that I had two problems. So, even with the long number it still accepts the quantity for ordering, so long as I round it.

Don't just round or normalise it to 2 digits. Do it the correct way as I have shown in the code above.

 
Sandstorm2: I always get  0.130000000000000004

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

See also The == operand. - MQL4 programming forum 2013.06.07

If you want to see the correct number of digits, convert it to a string with the correct/wanted accuracy.
          question about decima of marketinfo() - MQL4 programming forum 2016.05.18

 

The function MathRound() has some accuracy problems.

Edit:

The function MathRound() has some accuracy problems if used as is without any correction for floating point roundoff errors. 

The function NormalizeDouble() is better optimized for rounding of calculated prices/lots.

Here is a test script to show the accuracy issues and correct usage.

   //double lotstep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double lotstep = 0.01;
   double lots    = 2.405;

   //--- Round calculated lots to the nearest multiple of lotstep.
   double rounded_1 = NormalizeDouble(lots/lotstep, 0) * lotstep;

   //--- Less accurate rounding with edge numbers.
   double rounded_2 = MathRound(lots/lotstep) * lotstep;

   Print(lots);          // output: 2.405
   Print(rounded_1);     // output: 2.41
   Print(rounded_2);     // output: 2.4 (Less accurate)


This is my function for the accurate rounding of lots.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double RoundVolume(double pVolume, string pSymbol = NULL)
  {
   pSymbol = pSymbol == NULL ? _Symbol : pSymbol;

   double minlot  = SymbolInfoDouble(pSymbol, SYMBOL_VOLUME_MIN);
   double maxlot  = SymbolInfoDouble(pSymbol, SYMBOL_VOLUME_MAX);
   double lotstep = SymbolInfoDouble(pSymbol, SYMBOL_VOLUME_STEP);

   if(minlot == 0 || maxlot == 0 || lotstep == 0)
     {
      Print(__FUNCTION__, ": error, cannot retrieve volume info for " + pSymbol);
      return (0);
     }

   const double Epsilon = 0.000001;

   pVolume -= minlot;

   if(pVolume < -Epsilon)
     {
      return (0);
     }

   if(pVolume < Epsilon)
     {
      return (minlot);
     }

   //--- Round calculated lots to the nearest multiple of lotstep.
   pVolume = minlot + NormalizeDouble(pVolume/lotstep, 0) * lotstep;

   if(pVolume > maxlot)
     {
      pVolume = maxlot;
     }

   return (pVolume);
  }
 
amrali: The function MathRound() has some accuracy problems. The function NormalizeDouble() is better optimized for rounding of calculated prices/lots. Here is a test script to show the accuracy issues and correct usage. This is my function for the accurate rounding of lots.

No, MathRound is not less accurate. There is a rounding rule which states that when the digit is a 5, it should be rounded to the "even" number instead of the "odd" number. It is NormalizeDouble that is not working correctly by not applying this rule of rounding.

When rounding, you examine the digit following (i.e., to the right of) the digit that is to be the last digit in the rounded off number. The digit you are examining is the first digit to be dropped.

  1. If that first digit to be dropped is less than 5 (that is, 1, 2, 3 or 4), drop it and all the digits to the right of it.
  2. If that first digit to be dropped is more than 5 (that is, 6, 7, 8 or 9), increase by 1 the number to be rounded, that is, the preceding figure (to the digit being dropped).
  3. If that first digit to be dropped is 5, round the digit that is to rounded off so that it will be even. Keep in mind that zero is considered to be even when rounding off.

So in this case, the correct rounding for 2.405 is 2.40, not 2.41 (in accordance to above rules).

However, the above points are moot, because as a "double", 2.405 would actually be represented as "2.40499999999999980460074766597245", and ( 2.405 / 0.01 ), is represented as "240.49999999999997157829056959599257", so NormalizeDouble is totally incorrect!


 

Dear Fernando Carreiro,

I don't totally agree with your explanation. Given arbitrary values for lots (for example 2.085, 2.175, 2.405), you cannot predict the output of decimal rounding with MathRound().

Try to round those values (to a step of 0.01 lot) in your head before looking at the code and compare your results.   

Here is a script to further explain the inconstancies of MathRound():

Edit:

Round3 that applies (1+DBL_EPSILON) to MathRound() has well predicted results. It follows the 'Round half away from zero' rule and it gives exactly the same output as NormalizeDouble() for any floating point number.

#define Round1(lots, lotstep) (NormalizeDouble(lots/lotstep, 0) * lotstep)
#define Round2(lots, lotstep) (MathRound(lots/lotstep) * lotstep)
#define Round3(lots, lotstep) (MathRound(lots/lotstep*(1+DBL_EPSILON)) * lotstep)
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   //double lotstep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double lotstep = 0.01;

//--- NormalizeDouble() always gives a predictable output
   Print("lots = 2.085, Round1() = ", Round1(2.085, lotstep));     // output: 2.09 (Round half away from zero)
   Print("lots = 2.175, Round1() = ", Round1(2.175, lotstep));     // output: 2.18 (Round half away from zero)
   Print("lots = 2.405, Round1() = ", Round1(2.405, lotstep));     // output: 2.41 (Round half away from zero)
   Print("-----------");

//--- MathRound() has an inconsistent rounding mode
   Print("lots = 2.085, Round2() = ", Round2(2.085, lotstep));     // output: 2.09 (Round half away from zero)
   Print("lots = 2.175, Round2() = ", Round2(2.175, lotstep));     // output: 2.17 (Round half to odd)
   Print("lots = 2.405, Round2() = ", Round2(2.405, lotstep));     // output: 2.4  (Round half to even)
   Print("-----------");

//--- Fix to MathRound() to follow 'Round half away from zero' rule
   Print("lots = 2.085, Round3() = ", Round3(2.085, lotstep));     // output: 2.09 (Round half away from zero)
   Print("lots = 2.175, Round3() = ", Round3(2.175, lotstep));     // output: 2.18 (Round half away from zero)
   Print("lots = 2.405, Round3() = ", Round3(2.405, lotstep));     // output: 2.41 (Round half away from zero)
  }
//+------------------------------------------------------------------+

/* output:
  lots = 2.085, Round1() = 2.09
  lots = 2.175, Round1() = 2.18
  lots = 2.405, Round1() = 2.41
  -----------
  lots = 2.085, Round2() = 2.09
  lots = 2.175, Round2() = 2.17
  lots = 2.405, Round2() = 2.4
  -----------
  lots = 2.085, Round3() = 2.09
  lots = 2.175, Round3() = 2.18
  lots = 2.405, Round3() = 2.41
*/

NormalizeDouble() always follows a consistent rounding mode (Round half away from zero) because it is optimized to handle floating-point roundoff errors.

And, to clarify things, any of MathRound() /  NormalizeDouble() can be used to round decimal numbers (although in different ways), so that the trading server will not complain.

The main issue against using MathRound() for decimal rounding is inconsistent results and poor code predictability.

Edit: 

I mean using MathRound() without (1+DBL_EPSILON) for decimal rounding has inconsistent results because it does not follow any well-known rounding rule. 

By the way, in MetaTrader 5 the function DoubleToString() uses internally the same rounding rule as NormalizeDouble().

Edit: the rule is 'Round half away from zero' 

void OnStart()
  {
   int digits = 2;

//--- NormalizeDouble() follows 'Round half away from zero' rule
   Print("number = 2.085, NormalizeDouble() = ", NormalizeDouble(2.085, digits));
   Print("number = 2.175, NormalizeDouble() = ", NormalizeDouble(2.175, digits));
   Print("number = 2.405, NormalizeDouble() = ", NormalizeDouble(2.405, digits));
   Print("-----------");

//--- DoubleToString() follows 'Round half away from zero' rule
   Print("number = 2.085, DoubleToString() = ", DoubleToString(2.085, digits));
   Print("number = 2.175, DoubleToString() = ", DoubleToString(2.175, digits));
   Print("number = 2.405, DoubleToString() = ", DoubleToString(2.405, digits));
  }

/* output:
  number = 2.085, NormalizeDouble() = 2.09
  number = 2.175, NormalizeDouble() = 2.18
  number = 2.405, NormalizeDouble() = 2.41
  -----------
  number = 2.085, DoubleToString() = 2.09
  number = 2.175, DoubleToString() = 2.18
  number = 2.405, DoubleToString() = 2.41
*/

Rounding Modes:


Fixes for decimal rounding with MathRound() 

https://stackoverflow.com/a/48764436


My best regards,

 
amrali: Dear Fernando Carreiro, I don't totally agree with your explanation. Given arbitrary values for lots (for example 2.085, 2.175, 2.405), you cannot predict the output of decimal rounding with MathRound()...

Looks like you may be right. I had originally believed MathRound to be following the "Round to Even" convention (like in C#), but its not. I decided to lookup the "C" convention, which states the following rule:

  • If decimal value is from ”.1 to .5″, it returns integer value less than the argument. If decimal value is from “.6 to .9″, it returns the integer value greater than the argument.

However, looking at the output, its NOT following that convention either. I guess it is following another convention, but don't see which one. As you said, it does seem unpredictable, but I am sure it is following some convention but we just don't know which one.

For precision's sake, I outputted all the values and intermediate steps in high precision and I got the following:

Value   2.085 is represented as   2.08499999999999996447286321199499070644378662109375000000
Value 208.500 is represented as 208.50000000000000000000000000000000000000000000000000000000
NormalizeDouble(    208.500 ) = 209.00000000000000000000000000000000000000000000000000000000
MathRound(          208.500 ) = 209.00000000000000000000000000000000000000000000000000000000
MathRound EPSILON ( 208.500 ) = 209.00000000000000000000000000000000000000000000000000000000
Round1 =   2.08999999999999985789145284797996282577514648437500000000
Round2 =   2.08999999999999985789145284797996282577514648437500000000
Round3 =   2.08999999999999985789145284797996282577514648437500000000

Value   2.175 is represented as   2.17499999999999982236431605997495353221893310546875000000
Value 217.500 is represented as 217.49999999999997157829056959599256515502929687500000000000
NormalizeDouble(    217.500 ) = 218.00000000000000000000000000000000000000000000000000000000
MathRound(          217.500 ) = 217.00000000000000000000000000000000000000000000000000000000
MathRound EPSILON ( 217.500 ) = 218.00000000000000000000000000000000000000000000000000000000
Round1 =   2.18000000000000015987211554602254182100296020507812500000
Round2 =   2.16999999999999992894572642398998141288757324218750000000
Round3 =   2.18000000000000015987211554602254182100296020507812500000

Value   2.405 is represented as   2.40499999999999980460074766597244888544082641601562500000
Value 240.500 is represented as 240.49999999999997157829056959599256515502929687500000000000
NormalizeDouble(    240.500 ) = 241.00000000000000000000000000000000000000000000000000000000
MathRound(          240.500 ) = 240.00000000000000000000000000000000000000000000000000000000
MathRound EPSILON ( 240.500 ) = 241.00000000000000000000000000000000000000000000000000000000
Round1 =   2.41000000000000014210854715202003717422485351562500000000
Round2 =   2.39999999999999991118215802998747676610946655273437500000
Round3 =   2.41000000000000014210854715202003717422485351562500000000
 
Let me elaborate on that further to clear things for you. 
MathRound() itself always follows a fixed rounding rule to round to whole integers as stated in the language documentation. 

In Mql5, it follows the 'Round half away from zero'. In Javascript: round half up. In C#: default is round half to even, but it can be specified. 

However, the inconsistency problems arise when you try to use MathRound naively to round to decimal numbers without some correction for binary floating point roundoff errors (representation errors) . 

So, for the accurate decimal rounding, you have to apply the appropriate correction for MathRound(). Or, in Mql5 language there is the ready-made function NormalizeDouble() which is optimized for handling of decimal rounding. 
 
amrali: Let me elaborate on that further to clear things for you. MathRound() itself always follows a fixed rounding rule to round to whole integers as stated in the language documentation. 

However, the inconsistency problems arise when you try to use MathRound naively to round to decimal numbers due to the binary floating point roundoff errors (representation errors) . 

So, for the accurate decimal rounding, you have to apply the appropriate correction for MathRound(). 

Mql5 language has a ready-made function NormalizeDouble() better optimized for decimal rounding. 

Sorry, but I am not quite following you! What convention is it using exactly?

"rounding rule to round to whole integers" is not a convention, but just what its function is.

As for trying to adjust for representation or round-off errors, I personally prefer that a rounding functions DOES NOT try to do that.

The only thing I am not clear from your statement, is what convention is used by "MathRound". Its not the "C" convention, nor is it the "Round to Even" convention!

Do you know which convention it uses?

EDIT: Looks like you edited your post after I answered. So its using the "Round half away from zero". Thanks for that info!

EDIT2: The reason, I like having the rounding NOT adjust for corrections, is because I calculate my lots based on self-adjusting fractional risk percentages, so I prefer that it does not try to adjust. That is my preference obviously, not that of other traders.

Reason: