错误、漏洞、问题 - 页 2822

 
Nikolai Semko:

只有四舍五入没有使用标准的round(), ceil(), floor(),因为它们也是返回双数。

但通过这些,特别是它们比普通的工作更快。

它可能更快,但它就是错的。
将12345.0000000000001之类的东西传入你的ceil(与你的例子类似),你可以在输出中得到12346。
 
Alexey Navoykov:
它可能会更快,但它就是错的。
将12345.0000000000001这样的东西(与你的例子类似)传入你的ceil,你可以在输出中得到12346。

你自己试过吗?
试试吧。

Print(ceil( 12345.0000000000001));
Print(Ceil( 12345.0000000000001));
Print(ceil( 12345.000000000001));
Print(Ceil( 12345.000000000001));

输出。

2020.08.10 12:03:23.856 ErrorNormalizeDouble (EURUSD,M1)        12345.0
2020.08.10 12:03:23.856 ErrorNormalizeDouble (EURUSD,M1)        12345
2020.08.10 12:03:23.856 ErrorNormalizeDouble (EURUSD,M1)        12346.0
2020.08.10 12:03:23.856 ErrorNormalizeDouble (EURUSD,M1)        12346
应该是12346,因为它是一个ceil("从上面返回最接近的整数数字值")
,第一种情况是12345,因为双倍型的有效数字是17,而你有18
 
Nikolai Semko:

真的,你不能比较双打。这只是一个硬性规定。

当然,有可能,有时甚至有必要将双胞胎直接相互比较。

例如,在优化过程中,OnTick有时会被调用一万亿次。为了了解是否执行一个待定限价,内置的测试器会比较当前对应的符号价格和限价。它在每次调用OnTick前对每个挂单进行处理。也就是说,这些检查要做几百、几千亿次。

而且每次都是通过规范化来完成。嗯,这是一种可怕的计算资源 浪费。由于挂单的价格和符号被初步规范化。因此,它们可以而且应该直接相互比较。

MQL-定制的MQL测试器在性能上很容易胜过本地内置的测试器。

 

fxsaber
:

当然,有可能,有时甚至有必要将双胞胎直接相互比较。

例如,在优化过程中,OnTick有时会被调用一万亿次。内置的测试器,为了了解是否执行一个待定的限制,比较当前对应的符号价格和限制价格。它在每次调用OnTick前对每个挂单进行处理。也就是说,这些检查要做几百、几千亿次。

而且每次都是通过规范化来完成。嗯,这是一种可怕的计算资源 浪费。由于挂单的价格和符号被初步规范化。因此,它们可以而且应该直接相互比较。

MQL-定制的MQL测试器在性能上很容易胜过本地内置的测试器。

NormalizeDouble()是一个非常昂贵的函数。因此,你最好忘记它。

这里有一个脚本,演示了NormalizeDouble()和normalize with int之间的区别。

#define   SIZE 1000000

int Ceil (double x) {return (x-(int)x>0)?(int)x+1:(int)x;}
int Round(double x) {return (x>0)?(int)(x+0.5):(int)(x-0.5);}
int Floor(double x) {return (x>0)?(int)x:((int)x-x>0)?(int)x-1:(int)x;}
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   double a[SIZE];
   double s1=0,s2=0, s3=0;
   for (int i=0;i<SIZE;i++)  a[i]=(rand()-16384)/M_PI;
   
   ulong t1=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s1+=a[i];
   t1=GetMicrosecondCount()-t1;  
   
   ulong t2=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s2+=NormalizeDouble(a[i],5);
   t2=GetMicrosecondCount()-t2; 
   
   ulong t3=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s3+=Round(a[i]*100000);
   s3/=100000;
   t3=GetMicrosecondCount()-t3; 
   
   Print("простая сумма                            - " + string(t1)+ " микросекунд, сумма = "+ DoubleToString(s1,18));
   Print("сумма с NormalizeDouble                  - " + string(t2)+ " микросекунд, сумма = "+ DoubleToString(s2,18));
   Print("сумма, нормализированная через int       - " + string(t3)+ " микросекунд, сумма = "+ DoubleToString(s3,18));
  }

结果。

2020.08.10 12:55:30.766 TestSpeedNormalizeDouble (USDCAD,H4)    простая сумма                            - 1394 микросекунд, сумма = 626010.5038610587362201
2020.08.10 12:55:30.766 TestSpeedNormalizeDouble (USDCAD,H4)    сумма с NormalizeDouble                  - 5363 микросекунд, сумма = 626010.5046099 795727060
2020.08.10 12:55:30.766 TestSpeedNormalizeDouble (USDCAD,H4)    сумма, нормализированная через int       - 1733 микросекунд, сумма = 626010.5046099999 453873
SZZ的归一化也更准确(你可以通过归一化的最后一位数字后的9的数量看到它--用蓝色突出显示)。
 
Nikolai Semko:

NormalizeDouble()是一个非常昂贵的函数。这就是为什么最好是忘记它。

这里有一个脚本,演示了NormalizeDouble()和Normalize with int之间的区别。

结果。

SZZ通过int进行的归一化甚至更准确(你可以通过归一化最后一位数字后的9的数量看出这一点--用蓝色突出显示)。

如果不是通过双数,而是通过长数求和,那么结果会更令人印象深刻,因为通过int求和(乘法和四舍五入,然后除以最终的总和)的计算速度比正常的双数求和快。

#define   SIZE 1000000

int Ceil (double x) {return (x-(int)x>0)?(int)x+1:(int)x;}
int Round(double x) {return (x>0)?(int)(x+0.5):(int)(x-0.5);}
int Floor(double x) {return (x>0)?(int)x:((int)x-x>0)?(int)x-1:(int)x;}
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   double a[SIZE];
   double s1=0,s2=0, s3=0;
   long s=0;
   for (int i=0;i<SIZE;i++)  a[i]=(rand()-16384)/M_PI;
   
   ulong t1=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s1+=a[i];
   t1=GetMicrosecondCount()-t1;  
   
   ulong t2=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s2+=NormalizeDouble(a[i],5);
   t2=GetMicrosecondCount()-t2; 
   
   ulong t3=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) s+=Round(a[i]*100000);
   s3=s/100000.0;
   t3=GetMicrosecondCount()-t3; 
   
   Print("простая сумма                            - " + string(t1)+ " микросекунд, сумма = "+ DoubleToString(s1,18));
   Print("сумма с NormalizeDouble                  - " + string(t2)+ " микросекунд, сумма = "+ DoubleToString(s2,18));
   Print("сумма, нормализированная через int       - " + string(t3)+ " микросекунд, сумма = "+ DoubleToString(s3,18));  
  }

结果。

2020.08.10 13:15:58.982 TestSpeedNormalizeDouble (USDCAD,H4)    простая сумма                            - 1408 микросекунд, сумма = 460384.3207830497995019
2020.08.10 13:15:58.982 TestSpeedNormalizeDouble (USDCAD,H4)    сумма с NormalizeDouble                  - 6277 микросекунд, сумма = 460384.3162300114054233
2020.08.10 13:15:58.982 TestSpeedNormalizeDouble (USDCAD,H4)    сумма, нормализированная через int       - 964 микросекунд,  сумма = 460384.3162299999967218
 
Nikolai Semko:

如果求和不是通过double,而是通过long,那么结果会更令人印象深刻,因为通过int求和(乘法和四舍五入,然后再除以总和)比普通的double求和更快。

结果。

小数 ,用于比较添加。

链接错了,这不是一个完整的实现。

 
fxsaber:

而且每次都是通过正常化来完成。嗯,这是对计算资源 的严重浪费。

你怎么知道的? 因为即使价格没有被规范化,检查也只是在没有任何规范化的情况下进行。

 if (fabs(price-limitprice) < ticksize/2)

鉴于价格是ticksize的倍数

 
Nikolai Semko:
此外,通过int进行的归一化也被证明是更准确的(你可以通过归一化的最后一个数字后的9的数量看到它--用蓝色突出显示)。

这个测试是不正确的。 为什么你只在最后 除以100000.0一次? 应该在每次迭代时进行,然后求和。 这是一个公平的比较。 但这根本不是归一化 - 你只是优化了你的测试算法自然,它将更快、更准确(因为累积误差减少了)。

 
Alexey Navoykov:

你怎么知道这些?

因为你可以向测试仪输入非正常化的价格,它将以相同的方式处理它们。

毕竟,即使价格没有规范化,在没有任何规范化的情况下,检查也很容易完成。

在这种情况下,我指的是一个单一的标准算法,应用它之后,你可以直接比较这个标准的双打。

所以测试者并不直接比较双打。它是通过NormalizeDouble、ticksize或其他东西来实现的。但肯定不是通过直接比较双打。而且这一点也不理性。

 
fxsaber:

当然,有可能,有时甚至有必要将双胞胎直接相互比较。

例如,Optimize OnTick有时会被调用一万亿次。内置的测试器,为了了解是否执行悬挂的限制,对当前对应的符号价格和限制价格进行比较。它在每次调用OnTick前对每个挂单进行处理。也就是说,这些检查要做几百、几千亿次。

而且每次都是通过规范化来完成。嗯,这是一种可怕的计算资源 浪费。由于挂单的价格和符号被初步规范化。因此,它们可以而且应该直接相互比较。

MQL-定制的MQL测试器在性能上不比本地内置的测试器差。

所以我决定检查一下性能的无稽之谈版本。
结果令人惊讶。
,即使是预规范化的双倍数比较,也比通过epsilon或转换为int的双倍数比较时平均速度更慢。

#define  SIZE 1000000

int Ceil (double x) {return (x-(int)x>0)?(int)x+1:(int)x;}
int Round(double x) {return (x>0)?(int)(x+0.5):(int)(x-0.5);}
int Floor(double x) {return (x>0)?(int)x:((int)x-x>0)?(int)x-1:(int)x;}

bool is_equal(double d1, double d2, double e=0.000000001) {return fabs(d1-d2)<e;}

void OnStart()
  {
   double a[SIZE], a_norm[SIZE];
   int s1=0,s2=0, s3=0;
   for (int i=0;i<SIZE;i++)  {
     a[i]=(rand()-16384)/1641.1452;
     a_norm[i]=NormalizeDouble(a[i],2);
   }
   double test = 1.11;
   
   ulong t1=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) if (a_norm[i]==test) s1++;
   t1=GetMicrosecondCount()-t1;  
   
   ulong t2=GetMicrosecondCount();
   for (int i=0;i<SIZE;i++) if (is_equal(a[i],test,0.005)) s2++;
   t2=GetMicrosecondCount()-t2; 
   
   ulong t3=GetMicrosecondCount();
   int test_int = test*100;
   for (int i=0;i<SIZE;i++) if (Round(a[i]*100)==test_int) s3++;
   t3=GetMicrosecondCount()-t3; 
   
   
   Print("простое сравнение предварительно нормализированых double - " + string(t1)+ " микросекунд, всего совпадений = "+ string(s1));
   Print("сравнение double через эпсилон                           - " + string(t2)+ " микросекунд, всего совпадений = "+ string(s2));
   Print("сравнение double через преобразование в int              - " + string(t3)+ " микросекунд, всего совпадений = "+ string(s3));  
  }

其结果是。

2020.08.10 14:31:39.620 TestCompareDouble (USDCAD,H4)   простое сравнение предварительно нормализированых double - 900  микросекунд, всего совпадений = 486
2020.08.10 14:31:39.620 TestCompareDouble (USDCAD,H4)   сравнение double через эпсилон                           - 723  микросекунд, всего совпадений = 486
2020.08.10 14:31:39.620 TestCompareDouble (USDCAD,H4)   сравнение double через преобразование в int              - 805  микросекунд, всего совпадений = 486
2020.08.10 14:31:42.607 TestCompareDouble (USDCAD,H4)   простое сравнение предварительно нормализированых double - 1533 микросекунд, всего совпадений = 488
2020.08.10 14:31:42.607 TestCompareDouble (USDCAD,H4)   сравнение double через эпсилон                           - 758  микросекунд, всего совпадений = 488
2020.08.10 14:31:42.607 TestCompareDouble (USDCAD,H4)   сравнение double через преобразование в int              - 790  микросекунд, всего совпадений = 488
2020.08.10 14:31:44.638 TestCompareDouble (USDCAD,H4)   простое сравнение предварительно нормализированых double - 986  микросекунд, всего совпадений = 472
2020.08.10 14:31:44.638 TestCompareDouble (USDCAD,H4)   сравнение double через эпсилон                           - 722  микросекунд, всего совпадений = 472
2020.08.10 14:31:44.638 TestCompareDouble (USDCAD,H4)   сравнение double через преобразование в int              - 834  микросекунд, всего совпадений = 472

我不排除这在很大程度上取决于处理器的新颖性和结构,对某人来说,结果可能是不同的。

告诉你真相--我甚至不明白为什么会发生这种情况。
,似乎编译器对随机数之和没有任何优化。你不能把四舍五入放在括号外。
似乎处理器中的双倍数比较是一个命令
当通过epsilon(最快的方式)进行比较时,我们仍然有一个两个双倍数的比较操作,但此外我们还有一个传递三个参数的函数调用和一个减法操作。
两个双数的比较操作的性能是否取决于变量本身的值?我怀疑这一点。
天啊,我不明白。请帮助我,我有什么地方没有考虑到,或者我哪里做错了?