Can't round a number "0.13" - page 2

 
Fernando Carreiro:

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"

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

MQL MathRound() follows the 'Round half away from zero' rule

The function returns a value rounded off to the nearest integer of the specified numeric value.


#define PRINT(A) Print(#A + " = ", (A))

void OnStart()
  {
   // Positive numbers
   PRINT(MathRound(1.7));
   PRINT(MathRound(1.5));
   PRINT(MathRound(1.3));

   // Negative numbers
   PRINT(MathRound(-1.3));
   PRINT(MathRound(-1.5));
   PRINT(MathRound(-1.7));
  }

/* output:
  MathRound(1.7) = 2.0
  MathRound(1.5) = 2.0
  MathRound(1.3) = 1.0
  MathRound(-1.3) = -1.0
  MathRound(-1.5) = -2.0
  MathRound(-1.7) = -2.0
*/
 
amrali: MQL MathRound() follows the 'Round half away from zero' rule

Thanks for that info!

 

I clarify those issues because it find a lot of confusion on the forum about the two functions.

As you stated, selecting a rounding mode is a matter of personal preference. But, as I said earlier I prefer more consistent code.

Thanks.

 
amrali: I clarify those issues because it find a lot of confusion on the forum about the two functions. As you stated, selecting a rounding mode is a matter of personal preference. But, as I said earlier I prefer more consistent code.
Yes, I understand your point of view now. My preference is different to yours, but I do understand your reasoning.
 

Your expectations of the functions are wrong or cannot be fulfilled in principle, because of the way numbers are represented on the computer.

Integers (int, uint, long) have a 1 to 1 correspondence. So the calculator shows exactly what you expect to see, 1 as 1 and not as 0.999999999999999999999999999999999998 (or so).

Doubles are implemented as the CLOSEST number that results from formulas like this (see: #4):



Thus doubles are only as you would expect after a Normalize() if the number INCIDENTALLY results exactly from the formula. This gives only two solutions:

  1. for a visually smooth display you have to convert the number into a string in a dedicated way (DoubleToString(x,3)) and
  2. you understand the background and learn to deal with it - you can't get rid of it. MQL5 has done some things over time to catch the problems e.g. when comparing doubles, but these are convenient isolated solutions that don't change the principle.
  3. MathRound(x) does more or less: first add 0.5 an then cut of all the decimal after the decimal dot. But as long it is a double you can't get rid of decimals.
Can't round a number "0.13"
Can't round a number "0.13"
  • 2021.05.14
  • www.mql5.com
I can't place an order because I get an error that there's too many digits in the quantity...
 

Carl Schreiber:

MathRound(x) does more or less: first add 0.5 an then cut of all the decimal after the decimal dot. But as long it is a double you can't get rid of decimals.

MQL MathRound() is most likely implemented as:

double MathRound(double value)
  {
   return (double) (value > 0 ? (long)(value + 0.5) : (long)(value - 0.5));
  }

The function returns a value rounded off to the nearest integer of the specified numeric value.

MQL MathRound() follows the 'Round half away from zero' rule.


 

Using the following function or a similar expression for decimal rounding gives inconsistent results as it does not follow any well-known rounding rule.

The rounding rule is inconsistent due to binary floating-point roundoff errors in the intermediate result of number / step. 

double RoundToStep(double number, double step)
  {
   return MathRound(number / step) * step;
  }


These functions return a value rounded off to the nearest integer multiple of the specified decimal step.

The rounding is consistent and follows the 'Round half away from zero' rule.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double RoundToStep(double number, double step)
  {
   return NormalizeDouble(number / step, 0) * step;
  }
  
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double RoundToStep(double number, double step)
  {
   return MathRound(number / step * (1 + DBL_EPSILON)) * step;
  }
  
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double RoundToStep(double number, double step)
  {
   union _dbl {double value; long bits;} dbl;
   dbl.value = number / step;

//--- Increment the integer representation of the intermediate result
//--- to move to the next representable value away from zero.
   dbl.bits++;

   return MathRound(dbl.value) * step;
  }

Rounding to a specified multiple:


 

It is worth noting that binary floating-point roundoff errors in the intermediate results can also affect MathFloor() and MathCeil(), when used to round down/up a quantity to a specified multiple without taking the necessary precautions to ensure mathematically valid results.

Here is a test script to demonstrate how errors can occur even in an apparently simple calculation:

#include <math_utils.mqh>  // for RoundToStepDown()
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double NaiveFloor(double value, double step)
  {
   return (MathFloor(value/step) * step);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   //double lotstep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double lotstep = 0.01;
   double lots    = 1.15;

   double f1 = NaiveFloor(lots, lotstep);
   double f2 = RoundToStepDown(lots, lotstep);

   Print("lots = ", lots, ", NaiveFloor() = ",      f1);    // incorrect result!
   Print("lots = ", lots, ", RoundToStepDown() = ", f2);    // correct: value is already rounded
  }
//+------------------------------------------------------------------+
/* output:
  lots = 1.15, NaiveFloor() = 1.14
  lots = 1.15, RoundToStepDown() = 1.15
*/

The Math Utils - library provides useful functions for comparison and rounding of floating-point numbers (prices, lots and money).

The library implements more advanced fixes to MQL rounding functions to ensure mathematically sound results. 

Math Utils (MT5)

https://www.mql5.com/en/code/20822

Math Utils (MT4)

https://www.mql5.com/en/code/25723

Math Utils
Math Utils
  • www.mql5.com
Handy functions for comparison and rounding of floating-point numbers (prices, lots and money).
 
I don't know who is doing it, but it's very annoying to see this topic coming to the top or marked as unread to each time notice nothing new was posted. Please stop that.
Reason: