函数ceil()、round()、floor()的执行速度

 

我想和程序员们分享一个意想不到的、简单而有用的发现。

四舍五入的功能。

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

已经证明是非常缓慢的。为了将四舍五入的过程加快4-5倍(根据我在MQL5中的测试),你可以用一个简单的替代方法来取代这些函数。

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

由于这些函数经常被用于大型和嵌套的循环中,性能的提高可以是相当显著的。

可能,调用一个函数的 事实是相当耗时的(存储不同的数据、地址等)。而在这种情况下,你可以不使用功能。

附带性能测试的脚本文件。

附加的文件:
TestSpeed.mq5  3 kb
 
Nikolai Semko:

我想和程序员们分享一个意想不到的、简单而有用的发现。

四舍五入的功能。

已经证明是非常缓慢的。为了将四舍五入的过程加快4-5倍(根据我在MQL5中的测试),你可以用一个简单的替代方法来取代这些函数。

由于这些函数经常被用于大型和嵌套的循环中,性能的提高可以是相当显著的。

可能,调用一个函数的 事实是相当耗时的(存储不同的数据、地址等)。而在这种情况下,你可以不使用功能。

附有性能测试的脚本文件。

只有我与这句话

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

我不同意这句话。根据数学规则,如果小数部分小于0.5,则四舍五入到下边。但如果你把0.5加到45.27,就会四舍五入到较高的位置。

 
Alexey Viktorov:

只是我不同意这句话。

我不同意。根据数学规则,如果小数部分小于0.5,则向下舍入。但如果你把0.5加到45.27,就会四舍五入到较高的位置。


#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:

只是我不同意这句话。

我不同意。根据数学规则,如果小数部分小于0.5,则向下舍入。但如果你把0.5加到45.27,就会四舍五入。


你很迷惑。我特意在例子中插入了一个验证码。

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

如果我错了,就会执行Print("oops...",x) 操作符。

试试吧--没关系的。

 
Alexey Viktorov:

我是唯一有这句话的人。

不同意。根据数学规则,如果小数部分小于0.5,则向下舍入。但如果你把0.5加到45.27,它就会被四舍五入到较高的位置。


拿着它去看看怎么样?)))int(45.27 + 0.5)怎么会得出46?同样的45人将被保留。

 
Lilita Bogachkova:


我不是在谈论速度。

 
Nikolai Semko:

你一定很困惑。我特意在例子中插入了一个检查码。

如果我错了,那么Print("oops...",x) 语句将被执行。

试试吧--没关系的。


但是,如果阵列没有事先填充数据速度会发生变化,这一点还是很有意思的


#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)," микросекунд");
  }
//+------------------------------------------------------------------+


并填写

#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:

检查一下怎么样?)))int(45.27 + 0.5)怎么会得出46?同样的45人将被保留。

我同意,我失去了思考的方向。我把它收回...

 
Lilita Bogachkova:

但有趣的是,如果阵列没有事先填充数据速度会发生变化


并填充它

这很简单。编译器忽略了这个命令。
y0[i]+=0; // !!!!!!!!!!!!!!
因为它不会改变任何东西。数组仍未被初始化。看起来,已经初始化的数组访问速度更快。在第一种情况下,初始化是在计算t1时在第二个循环中进行的,所以t1比t2要大。而在第二种情况下,初始化发生在第一个循环中。因此,t1和t2是相同的。
 

我觉得 "#define "更方便

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

你为什么不投向长?虽然你也可以溢出它,但溢出一个Int更容易。