Speed of execution of the functions ceil(),round(),floor()

 

I want to share an unexpected, simple and useful finding with programmers.

The rounding functions:

floor(), ceil(), round() 
они же
MathFloor(), MathCeil(),MathRound()

Have proven to be very slow. To speed up the rounding process by 4-5 times (according to my tests in MQL5), you can replace these functions with a simple alternative:

double x=45.27;
int y;
//работает только для положительных чисел!!! 
y=floor(x); -> y=(int)x;
y=ceil(x);  -> y=(x-(int)x>0)?(int)x+1:(int)x;   //более быстрым вариантом является: -> y=(int)(x+0.9999999999999997), но при этом возможна некорректная отработка
y=round(x); -> y=(int)(x+0.5);

Удобнее использовать #define.  Например:
#define _ceil(x) (x-(int)x>0)?(int)x+1:(int)x
y=_ceil(x);

// Для положительных и отрицательных чисел: ( выигрыш в скорости - в 3-4 раза)
#define _ceil(x) (x-(int)x>0)?(int)x+1:(int)x
#define _round(x) (x>0)?(int)(x+0.5):(int)(x-0.5)
#define _floor(x) (x>0)?(int)x:((int)x-x>0)?(int)x-1:(int)x

Since these functions are often used in large and nested loops, the performance gain can be quite significant.

Probably, the fact of calling a function is quite time-consuming (storing of different data, addresses, etc.). And in this case you can do without functions.

File of script with performance test attached.

Files:
TestSpeed.mq5  3 kb
 
Nikolai Semko:

I want to share an unexpected, simple and useful finding with programmers.

The rounding functions:

Have proven to be very slow. To speed up the rounding process by 4-5 times (according to my tests in MQL5), you can replace these functions with a simple alternative:

Since these functions are often used in large and nested loops, the performance gain can be quite significant.

Probably, the fact of calling a function is quite time-consuming (storing of different data, addresses, etc.). And in this case you can do without functions.

Script file with performance test attached.

Only I with this line

y=round(x); -> y=(int)(x+0.5);

I don't agree with this line. According to the rules of mathematics, if the fractional part is less than 0.5, rounding is done to the lower side. But if you add 0.5 to 45.27, it is rounded to the higher side.

 
Alexey Viktorov:

Only I don't agree with that line.

I don't agree. According to the rules of mathematics, if the fractional part is less than 0.5, it is rounded down. But if you add 0.5 to 45.27, it is rounded to the higher side.


#define  MUL(x) ((x)+(0.5)) 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   ulong t0,t1,t2,t3;
   int y0[],y1[],y2[];
   ArrayResize(y0,10000000);
   ArrayResize(y1,10000000);
   ArrayResize(y2,10000000);
   double x=1.45;  

   for(int i=0;i<10000000;i++)
     {
      if ((int)(x+0.5)!=(int)round(x)) Print("ой...",x);
      x+=0.27;
      y0[i]+=0;
      y1[i]+=0;
      y2[i]+=0;
     }
     Print("y0[]: ",y0[9999999]," / y1[]: ",y1[9999999]," / y2[]: ",y2[9999999]);
   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y0[i]=(int)MathRound(x);
      x+=0.27;
     }
   t1=GetMicrosecondCount()-t0;
   Print("y0[]: ",y0[9999999]);
   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y1[i]=(int)(x+0.5);
      x+=0.27;
     }
   t2=GetMicrosecondCount()-t0;
   Print("y1[]: ",y1[9999999]);
   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y2[i]=(int)MUL(x);
      x+=0.27;
     }
   t3=GetMicrosecondCount()-t0;
   Print("y2[]: ",y2[9999999]);
   Print("Цикл округления 10 000 000 раз: (round) = ",IntegerToString(t1),"   альтернатива с (int) = ",IntegerToString(t2),"   альтернатива с (#define) = ",IntegerToString(t3)," микросекунд");
  }
//+------------------------------------------------------------------+
 
Alexey Viktorov:

Only I don't agree with that line.

I don't agree. According to the rules of mathematics, if the fractional part is less than 0.5, it is rounded down. But if you add 0.5 to 45.27, it's rounded up to the higher side.


You're confused. I purposely put a check code in the example:

for(int i=0;i<10000000;i++)
     {
      if ((int)(x+0.5)!=(int)round(x)) Print("ой...",x);
      x+=0.27;
     }

If I was wrong, thePrint("oops...",x) operator would be executed;

Try it - it's OK.

 
Alexey Viktorov:

I'm the only one with that line.

don't agree. According to the rules of mathematics, if the fractional part is less than 0.5, it is rounded down. But if you add 0.5 to 45.27, it's rounded to the higher side.


How about taking it and checking it out? ))) How would int(45.27 + 0.5) give 46? The same 45 will remain.

 
Lilita Bogachkova:


I wasn't talking about speed.

 
Nikolai Semko:

You must be confused. I purposely inserted a check code in the example:

if I was wrong, then thePrint("oops...",x) statement would be executed;

Try it - it's OK.


But it's still interesting that the speed changes if the array isnot filled with databeforehand


#define _round(x) (int)((x)+(0.5)) 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   ulong t0,t1,t2;
   int y0[],y1[];
   ArrayResize(y0,10000000);

   double x=1.45;  

   for(int i=0;i<10000000;i++)
     {
      if ((int)(x+0.5)!=(int)round(x)) Print("ой...",x);
      x+=0.27;
      y0[i]+=0; // !!!!!!!!!!!!!!
     }

   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y0[i]=(int)(x+0.5);
      x+=0.27;
     }
   t1=GetMicrosecondCount()-t0;

   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y0[i]=_round(x);
      x+=0.27;
     }
   t2=GetMicrosecondCount()-t0;

   Print("Цикл округления 10 000 000 раз:  с (int) = ",IntegerToString(t1),"   с (#define) = ",IntegerToString(t2)," микросекунд");
  }
//+------------------------------------------------------------------+


and filled

#define _round(x) (int)((x)+(0.5)) 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   ulong t0,t1,t2;
   int y0[],y1[];
   ArrayResize(y0,10000000);

   double x=1.45;  

   for(int i=1;i<10000000;i++)
     {
      if ((int)(x+0.5)!=(int)round(x)) Print("ой...",x);
      x+=0.27;
      y0[i]+=1; // !!!!!!!!!!!!!!
     }

   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y0[i]=(int)(x+0.5);
      x+=0.27;
     }
   t1=GetMicrosecondCount()-t0;

   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y0[i]=_round(x);
      x+=0.27;
     }
   t2=GetMicrosecondCount()-t0;

   Print("Цикл округления 10 000 000 раз:  с (int) = ",IntegerToString(t1),"   с (#define) = ",IntegerToString(t2)," микросекунд");
  }
//+------------------------------------------------------------------+
 
Ihor Herasko:

How about checking it out? ))) How would int(45.27 + 0.5) give 46? The same 45 will remain.

I agree, I lost my train of thought. I take it back...

 
Lilita Bogachkova:

But it is still interesting that the speed changes if the arrayis not filled with databeforehand


and fill it

It's quite simple. The compiler ignores the command:
y0[i]+=0; // !!!!!!!!!!!!!!
because it doesn't change anything. The array remains uninitialised. It looks like the already initialized array is accessed faster. In the first case, initialization is performed in the second loop when calculating t1, so t1 is larger than t2. And in the second case initialization occurs in the first loop. Therefore, t1 and t2 are the same.
 

I find "#define" more convenient

#define _floor(x) (int)((x)) 
#define _ceil(x)  (int)((x)+(1)) 
#define _round(x) (int)((x)+(0.5)) 
 

Why don't you cast to the long? Although you can overflow it too, it's a lot easier to overflow an Int.

Reason: