价格!=价格? - 页 5

 

我从这个关于价格平等的基本前提开始(而不仅仅是双倍数的平等)--

(P1).假设y=1.50000:x==y,只要x是(i)大于或等于1.499995和(ii)小于1.500005的任何实数。

在P1的基础上,我得出结论:-

(P2).假设y=1.50000:a==y,b==y,以及a==b,只要a和b是(i)大于或等于1.499995和(ii)小于1.500005的实数。

例子包括:1.500055 == 1.50006,1.500055 == 1.500064,1.500051 != 1.500059,和1.500054 != 1.500056。

利用上述情况,我创建了一个函数(如下),(1)将两个价格作为参数,(2)将这些价格四舍五入到最近的等值点,(3)确定这两个价格是否相等。

bool IsEqual(double price1, double price2) {
   // Price Conditioning
   //    * this fixes the occurrence of 1.5000551 != 1.5000550
   price1 += Point * 0.0015;
   price2 += Point * 0.0015;
      
   int p1 = MathRound(price1 / Point),
       p2 = MathRound(price2 / Point);
          
   return (p1 == p2);
}

这个函数简单明了,但我应该对 "价格调理 "部分做一些评论。 正如我们许多人所知,双数(即。我发现,当我把1.5000551和1.5000550四舍五入到最近的点并比较结果(分别为1.50006和1.50005)时,它们似乎并不相等,尽管根据上述P1和P2,它们应该相等。 我得出结论(在进行了几次测试后),1.5000550的文字被存储在变量中为~1.5000549999。为了解决这个问题,我决定,如果价格在距离中间点(x.xxxx5)万分之十五以内,我将假定价格已经达到了四舍五入的最低门槛。因此,在四舍五入之前,我在每个价格上增加了万分之十五。 目前,我不认为这种增加有任何意外的后果。 此外,这些值可以调整,以增加/减少四舍五入的假设。

RaptorUK WHRoeder(以及其他人):以上述内容为蓝本,我根据RaptorUK之前的帖子构建了以下名为ComparePrices()的函数。

#define EQ      1
#define NEQ     2
#define LT      3
#define LToE    4
#define GT      5
#define GToE    6

bool ComparePrices(double FristPrice, double SecondPrice, int ComparisonType) {
   // Price Conditioning
   FirstPrice  += Point * 0.0015;
   SecondPrice += Point * 0.0015;
      
   int price1 = MathRound(FirstPrice / Point),
       price2 = MathRound(SecondPrice / Point);
                
   switch(ComparisonType) {
      case LToE: return (price1 < price2 || price1 == price2);
      case GToE: return (price1 > price2 || price1 == price2);
      case LT:   return (price1 < price2);
      case GT:   return (price1 > price2);
      case EQ:   return (price1 == price2);
      case NEQ:  return (price1 != price2);
      default:   return (false);
   }    
}

像往常一样,欢迎大家提出有指导性/建设性的意见。:)

 

我自己在这方面也做了一些尝试--试图在可读性和性能方面达到一个可接受的折中。


我已经解决了个别函数 eq(a,b), ne(a,b), lt(a,b)等。


鸡蛋

if (eq(a,b)) { ...}


关于在我的慢速虚拟机上进行4999999次迭代的性能,我得到了以下的基线测量。

空循环:370ms

inline MathAbs(a-b) < gHalfPoint (global) :2482ms

空的bool函数。4266ms < -- 我的目标是尽可能地接近这个数字。

我管理的最快的eq()实现如下。

它们比内联MathsAbs()调用慢2.3倍,比只返回真值的空布尔函数调用慢1.3倍。

另外,作为一个旁观者,我发现MQL并没有将布尔表达式短路。

bool eq(double a,double b) {

   if (a > b) {
      return ((a-b) < gpoint2);
   } else {
      return ((b-a) < gpoint2);
   }

}

5558ms

或者如果你喜欢静态的东西而不是globals(把所有代码放在一个地方)。

bool eq(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a > b) {
      return (a-b < p2);
   } else {
      return (b-a < p2);
   }
}

5718ms


lt(), gt()等应该更快,因为eq()和ne()更复杂。

 
RaptorUK: 那么,我怎样才能使TestValue等于1.57373,而不是>或<?

你不知道。浮点数对某些数字来说永远是精确的。

https://en.wikipedia.org/wiki/Floating_point

浮点数是有理数,因为它们可以被表示为一个整数除以另一个整数。例如,1.45×103 就是(145/100)*1000或145000/100。然而,基数决定了可以表示的分数。例如,1/5不能用二进制基数精确表示为浮点数,但可以用十进制基数精确表示(0.2,或2×10-1)。然而,1/3既不能用二进制(0.010101......)也不能用十进制(0.333....)精确表示,但在基数3 中却很简单(0.1或1×3-1 )。
这就是为什么我说永远,永远不要使用NormalizeDouble。它是个麻烦的东西。它的使用始终是错误的。
 
Thirteen:

case LToE: return (price1 < price2 || price1 == price2);
case GToE: return (price1 > price2 || price1 == price2);

经纪人的双倍值可以从1.23457500000000到1.2345849999999的任何地方,仍然被认为是相同的1.23458价格。

然而,你的函数说,1.23457500000000不是1.2345849999999的GToE。

然而,你的函数说,1.23458499999999不是1.23457500000000的LTOE。

你必须在比较中使用一个点/2https://www.mql5.com/en/forum/136997/page3#780837

 
ydrol:

我自己也玩了一下这个游戏--试图在可读性和性能方面达到一个可接受的折中。

我相信0.0是一个特殊情况,所以你可以直接用0.0进行测试。
 
WHRoeder:

经纪人的双倍值可以从1.23457500000000到1.234584999999999的任何地方,仍然被视为相同的1.23458价格。

我一般同意。 见我上述帖子 中的P1和P2。

WHRoeder

但你的函数说1.23457500000000不是1.2345849999999的GToE。

然而,你的函数说1.23458499999999不是1.23457500000000的LTOE。

问题出在MT4/MQL如何在变量中存储浮点值。 例如。

double p1 = 1.234575000000000000, p2 = 1.23458499999999999;
Print ("p1 = ", DoubleToStr(p1, 8), " p2 = ", DoubleToStr(p2, 8));

打印日志/日记中的两个变量。

比较价格测试#1

你可以看到,p2不再是1.234584999999999,而是变成了1.23458500--我相信是由于四舍五入的原因。 这就是为什么我的函数说p1与p2不是GToE;正如你在下面的代码中看到的,你的代码 也表明了同样的情况--即p1与p2不是GToE,p1不等于p2。

double p1 = 1.234575000000000000, p2 = 1.23458499999999999;
Print ("p1 = ", DoubleToStr(p1, 8), " p2 = ", DoubleToStr(p2, 8));
Print ("GToE: ", p1 >= p2);
Print ("ComparePrices() for GToE: ", ComparePrices(p1, p2, GToE));
Print ("WHRoeder GToE: ", p1 - p2 > -Point/2.);
Print ("WHRoeder NEQ: ", MathAbs(p1 - p2) > Point / 2.);

比较价格测试#2

你必须在比较中使用一个点/2

有一种可能是Point/2的最大偏差太小。 比如说。

double p1 = 1.234575000000000000, p2 = 1.23458499999999999, p3 = 1.234580;
Print ("p1 = ", DoubleToStr(p1, 8), " p2 = ", DoubleToStr(p2, 8), " p3 = ", DoubleToStr(p3, 8));
Print ("#1 WHRoeder NEQ: ", MathAbs(1.234575000000000000 - 1.23458499999999999) > Point / 2.);
Print ("#2 WHRoeder NEQ: ", MathAbs(p1 - p3) > Point / 2.);
Print ("#3 WHRoeder NEQ: ", MathAbs(p2 - p3) > Point / 2.);

比较价格测试#3

如果假设1.234575等于1.234580,那么为什么#2显示NEQ? 此外,如果我们假设1.23458是一个可能意味着来自经纪人的价格,从1.23457500000000到1.2345849999999,为什么#1显示NEQ? 如果它们共享相同的价格点,它们不应该是相等的吗(因此我在上面的帖子中的前提#2)?

 

@Thirteen,


在你的代码中,你看到的是由于应用逻辑造成的有意的四舍五入差异 ,而不是由于浮点错误造成的无意的四舍五入错误 ,因此有区别。

两种类型的 "四舍五入 "是。

a) 由于IEEE格式中的二进制分数造成的内在舍入错误 。 - 这些数字本来是完全一样的,但由于十进制分数的二进制表示法而不一样。它们是由小数的MQ4表示法舍入的。

b) 明确四舍五入到某些数字或小数位。(例如,当打印,或发送价格给经纪人时)。- 这些并不是真正意义上的相同值,相反,它们被应用逻辑四舍五入,以方便某人。

这并不是一个真正的错误。仅仅由于对浮点表示的错误不太可能会有这么大的 误差(除非计算系列的时候很糟糕)。但是你可能想在你的应用程序中根据你自己的逻辑做这种类型的比较。


本质上的四舍五入误差[a]通常非常小(比点的数量级小),而且是无意 的。应用程序无法通过使用double 数据类型将这些数字四舍五入为准确的预期值。

明确的四舍五入差异[b]是有意为之 的,而且大得多 (+/-0.5点)。(在这种情况下)。因此,两个被你的应用逻辑四舍五入到相同点值的数字最初可能相差整整一个点。


理想情况下,我会先对数字进行四舍五入[b](只有在需要四舍五入的情况下) ,然后再 对它们进行比较[a],这时由于双倍 的限制,误差会非常小。(例如:< 0.0000001)

但是你的代码是 四舍五入之前 进行比较,在这种情况下,你必须详细说明可能出现的更大的差异。然而,四舍五入并不总是需要的。我只在向经纪人发送价格时使用它。


换个角度想想(如果MQ4使用二进制编码的十进制--它允许精确表示十进制的分数--那么所有关于Price != Price的问题都会消失。

但你仍然需要在你的应用程序中对 某些操作 的数字进行四舍五入和比较到最近的点。(主要是订单XXX功能)


>> "如果我们假设1.23458是一个价格,这可能意味着经纪人的价格是在1.23457500000000到1.234584999999999之间的任何地方"

我可能是错的(不知道经纪人是如何工作的),但我认为来自经纪人的1.23458的价格正是如此。否则就会有很多钱(由经纪人)通过利用公布价格的差异来赚钱。

我的理解是,实际上只有在向经纪人发送时,你才必须进行四舍五入,而不是在整个应用中。在这种情况下,对小误差的比较应该足够了。

浮动点的不准确性与经纪人价格的四舍五入是分开的。但是,如果你想同时处理这两个问题,我想这是你的个人偏好(不过可能会引起混淆?)

 

这是我的完整版本,(希望没有bug)。

这提供了6个功能。

eq(a,b) =
ne(a,b) !=
gt(a,b) >
lt(a,b) <
ge(a,b) >=
le(a,b) <=

if (ge(Bid,target)) sell sell sell...


这样做的目的是为了保持代码的可读性(IMO),并减少输入错误的可能性,而不会对性能造成太大的影响。

就所有的意图和目的而言,这些函数应该和使用MQ4用户函数一样快。

(关于性能与MathAbs(a-b)< HalfPoint的比较,见https://www.mql5.com/en/forum/136997/page5#822505,尽管在真正的EA中(相对于基准),我怀疑差别是不明显的。


bool gt(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a < b) {
      return (false);
   } else {
      return (a-b > p2);
   }
}
bool lt(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a > b) {
      return (false);
   } else {
      return (b-a > p2);
   }
}
bool ge(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a >= b) {
      return (true);
   } else {
      return (b-a <= p2);
   }
}
bool le(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a <= b) {
      return (true);
   } else {
      return (a-b <= p2);
   }
}
bool eq(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a > b) {
      return (a-b <= p2);
   } else {
      return (b-a <= p2);
   }
}

bool ne(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a > b) {
      return ((a-b) > p2);
   } else {
      return ((b-a) > p2);
   }
}
 
ydrol:

这是我的完整版本,(希望没有bug)。

...

bool eq(double a,double b) {
   static double p2=0;
   if (p2==0) p2 = Point/2;
   
   if (a > b) {
      return (a-b <= p2);
   } else {
      return (b-a <= p2);
   }
}

常常被引用的前提是。

  • 经纪人的双倍值可以从1.23457500000000到1.234584999999999的任何地方,仍然被认为是同一个1.23458的价格。

考虑到这一前提,并以你的代码为背景,你能向我解释一下,为什么你说(a)1.234576和1.234584被认为不相等,(b)1.234577和1.234583被认为不相等,但(c)1.234578和1.234582被认为是相等? 为什么(以及如何)例子(b)比例子(c)更不平等?

正如我上面 所说,我认为所有这些价格都是相等的,因为每个价格都有相同的价位--即1.23458。 这个例子说明了为什么我相信(并在上面 说过),点/2可能是一个太小的最大偏差。

 

@Thirteen,我对你的观察的回答仍然是上面3个帖子https://www.mql5.com/en/forum/136997/page5#822672链接。我将重复可能导致理解我的观点的闪光点的部分。(有一点修改和强调)

Think of it another way (If MQ4 had used Binary Coded Decimal - which allows exact representation of Decimal fractions - then most of the original issues regarding Price != Price would go away, (and is often used on financial platforms for that very reason )

在某些操作 中,你仍然需要在你的应用程序中对数字进行舍入和比较到最近的点。(主要是OrderXXX功能)


这只是取决于你如何写你的代码,以及你是否想区分应用程序的四舍五入(为了简单/方便,两个不同的数字在概念上/逻辑上被视为相同)。

和浮点错误。这没有什么对错之分,但我认为一种方法比另一种方法更令人困惑....。


此外,我个人对前面的帖子中再次提到的非引用的前提有点怀疑(但愿意纠正!)。