가격 수 != 가격 ? - 페이지 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);
}

이 기능은 간단하고 간단하지만 "가격 조정" 부분에 대해 언급해야 합니다. 우리 중 많은 사람들이 알고 있듯이 double(즉, 배정밀도 부동 소수점 형식 변수)은 때때로 약간 부정확하고 가끔 반올림 문제가 있습니다. 1.5000551 및 1.5000550을 가장 가까운 점으로 반올림하고 결과(각각 1.50006 및 1.50005)를 비교했을 때 위의 P1 및 P2에서 동일해야 하지만 동일하지 않은 것으로 나타났습니다. 나는 (몇 가지 테스트를 수행한 후) 리터럴 1.5000550이 ~1.5000549999로 변수에 저장되었다고 결론지었습니다. 이 문제를 해결하기 위해 가격이 중간 지점(x.xxxxx5)에서 10,000분의 1 지점 이내이면 가격이 가장 가까운 지점으로 반올림하기 위한 최소 임계값을 충족했다고 가정합니다. 따라서 가장 가까운 소수점으로 반올림하기 전에 각 가격에 10,000포인트를 더합니다. 현재로서는 이 추가가 의도하지 않은 결과라고 생각하지 않습니다. 또한 이러한 값을 조정하여 가장 가까운 점으로 반올림하는 가정을 증가/감소시킬 수 있습니다.

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회 반복에 대한 느린 VM의 성능과 관련하여 다음과 같은 기준 측정값을 얻습니다.

빈 루프: 370ms

인라인 MathAbs(ab) < gHalfPoint(전역) : 2482ms

Empty bool 기능: 4266ms <-- 이 수치에 최대한 근접하는 것을 목표로 하고 있습니다.

내가 관리한 가장 빠른 eq() 구현은 다음과 같습니다.

인라인 MathsAbs() 호출보다 약 2.3배 느리고 true를 반환하는 빈 부울 함수 호출보다 1.3배 더 느립니다.

또한 제쳐두고 나는 MQL이 부울 표현식을 단락시키지 않는다는 것을 발견했습니다.

 bool eq( double a, double b) {

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

}

5558ms 에서

또는 전역보다 정적을 선호하는 경우(모든 코드를 한 곳에 유지하기 위해):

 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×10 3 은 (145/100)*1000 또는 145000/100입니다. 그러나 밑수는 나타낼 수 있는 분수를 결정합니다. 예를 들어, 1/5는 2진법을 사용하여 부동 소수점 숫자로 정확히 표현할 수 없지만 10진법(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.23457500000000000에서 1.2345849999999999 사이일 수 있으며 여전히 동일한 1.23458 가격으로 간주됩니다.

그러나 귀하의 기능은 1.234575000000000000이 1.23458499999999999의 GToE가 아니라고 말합니다.

그러나 귀하의 기능은 1.234584999999999999가 1.234575000000000000의 LToE가 아니라고 말합니다.

비교에서 포인트/2를 사용해야 합니다. https://www.mql5.com/en/forum/136997/page3#780837

 
ydrol :

가독성과 성능에 대한 수용 가능한 타협점에 도달하기 위해 노력했습니다.

0.0은 특별한 경우라고 생각하므로 0.0으로 직접 테스트할 수 있습니다.
 
WHRoeder :

중개인의 두 배 값은 1.23457500000000000에서 1.2345849999999999 사이일 수 있으며 여전히 동일한 1.23458 가격으로 간주됩니다.

나는 일반적으로 동의합니다. 내 위의 게시물 에서 내 P1 및 P2를 참조하십시오.

WHR로더 :

그러나 귀하의 기능은 1.234575000000000000이 1.23458499999999999의 GToE가 아니라고 말합니다.

그러나 귀하의 기능은 1.234584999999999999가 1.234575000000000000의 LToE가 아니라고 말합니다.

문제는 MT4/MQL이 부동 소수점 값을 변수에 저장하는 방법에서 발생합니다. 예를 들어:

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

로그/저널에 두 개의 변수를 인쇄합니다.

가격 비교 테스트 #1

보시다시피 p2는 더 이상 1.23458499999999999가 아니라 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.234575000000000000에서 1.23458499999999999 사이의 브로커 가격을 의미할 수 있는 가격이라고 가정하면 #1이 NEQ를 표시해야 하는 이유는 무엇입니까? 동일한 가격대를 공유하는 경우 동일해야 하지 않습니까(따라서 위의 게시물에서 내 전제 #2)?

 

@열셋,


코드에서 부동 소수점 오류로 인한 의도하지 않은 반올림 오류가 아니라 응용 프로그램 논리로 인한 의도적인 반올림 차이 를 보고 있으므로 다음과 같은 차이가 있습니다.

'반올림'의 두 가지 유형은 다음과 같습니다.

a) IEEE 형식의 이진 분수로 인한 고유 반올림 오류 . - 이 숫자는 정확히 동일해야 하지만 소수의 이진 표현으로 인한 것이 아닙니다. MQ4 소수점 표시로 반올림됩니다.

b) 일부 숫자 또는 소수 자릿수로 명시적으로 반올림합니다. (예: 인쇄할 때 또는 브로커에게 가격을 보낼 때). - 이들은 실제로 동일한 값을 의미하지 않으며 대신 누군가의 편의를 위해 애플리케이션 로직에 의해 반올림됩니다.

이것은 실제로 오류가 아닙니다. 부동 소수점 표현으로 인한 오류는 이렇게 커질 것 같지 않습니다 (시리즈를 잘못 계산하지 않는 한). 그러나 자신의 논리에 따라 응용 프로그램에서 이러한 유형의 비교를 수행할 수 있습니다.


본질적인 반올림 오류[a]는 일반적으로 매우 작고 ( Point 보다 수십 배 작음) 의도하지 않습니다. 응용 프로그램은 이중 데이터 유형을 사용하여 이러한 숫자를 정확히 의도한 값으로 반올림할 수 없습니다.

명시적 반올림 차이[b]는 의도적 이며 훨씬 더 큽니다 (+/- 0.5포인트). (이 경우). 따라서 애플리케이션 논리에 의해 동일한 포인트 값으로 반올림된 두 숫자는 원래 거의 1포인트 차이가 날 수 있습니다.


이상적으로는 숫자 [b]를 반올림한 다음(반올림이 필요한 경우에만) 두 값 비교합니다. 이때 double 의 제한으로 인해 오류가 매우 작습니다. (예: < 0.0000001)

그러나 코드는 반올림 하기 전에 비교하기 위한 것이므로 훨씬 더 큰 가능한 차이로 자세히 설명해야 합니다. 그러나 반올림이 항상 필요한 것은 아닙니다. 브로커에게 가격을 보낼 때만 사용합니다.


다른 방식으로 생각해 보십시오(MQ4가 십진수 분수의 정확한 표현을 허용하는 Binary Coded Decimal 을 사용했다면 Price != Price와 관련된 모든 문제가 사라질 것입니다.

그러나 여전히 특정 작업에 대해 응용 프로그램의 숫자를 반올림하고 가장 가까운 지점과 비교해야 합니다. (주로 OrderXXX 기능)


>> "1.23458이 1.234575000000000000에서 1.23458499999999999 사이에 있는 브로커의 가격을 의미할 수 있는 가격이라고 가정하면"

나는 여기에서 틀릴 수 있지만 (중개인이 어떻게 작동하는지 확실하지 않음) 중개인의 가격이 1.23458이라고 생각합니다. 특히 $100,000 로트 크기 이상을 고려해야 합니다. 그렇지 않으면 공개된 가격의 차이를 이용하여 (브로커가) 많은 돈을 벌 수 있습니다.

내 이해는 중개자에게 보낼 때만 반올림해야 하며 애플리케이션 전체가 아니라는 것입니다. 이 경우 작은 오류에 대한 비교로 충분합니다.

부동 소수점 부정확성은 브로커 가격 반올림과 별개입니다. 하지만 두 가지를 동시에 처리하고 싶다면 개인 취향인 것 같아요(혼란스러울 수 있음?)

 

여기 내 완전한 버전이 있습니다(버그가 없기를 바랍니다)..

이것은 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(ab) < 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 :

여기 내 완전한 버전이 있습니다(버그가 없기를 바랍니다)..

...

 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.23457500000000000에서 1.2345849999999999 사이일 수 있으며 여전히 동일한 1.23458 가격으로 간주됩니다.

그 전제를 고려하고 귀하의 코드를 배경으로 사용하면서 왜 (a) 1.234576 및 1.234584 가 같지 않은 것으로 간주되는지, (b) 1.234577 및 1.234583 이 같지 않은 것으로 간주 되는지 설명해주실 수 있습니까? 1.234578 및 1.234582 는 동일한 것으로 간주 됩니까? 왜 (그리고 어떻게) 예 (b)가 예 (c)보다 덜 동등합니까?

위에서 언급했듯이 각 가격은 동일한 가격대, 즉 1.23458을 공유하기 때문에 모든 가격이 동일하다고 생각합니다. 이 예는 Point/2가 최대 편차에 비해 너무 작을 수 있다고 내가 믿는(그리고 위에서 말한) 이유를 보여줍니다.

 

@Thirteen, 귀하의 관찰에 대한 제 답변은 https://www.mql5.com/en/forum/136997/page5#822672 링크 위의 동일한 3개의 게시물로 유지됩니다. 내 관점을 이해하는 데 전구 순간으로 이어질 수 있는 부분을 반복하겠습니다. (약간의 수정 및 강조 추가)

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 기능)


코드 작성 방법에 따라 다르며 애플리케이션 반올림(두 개의 다른 숫자가 단순성/편의성을 위해 개념적으로/논리적으로 동일하게 취급되는 경우)을 구별하려는 경우

부동 소수점 오류. 옳고 그름은 없지만 한 접근 방식이 다른 접근 방식보다 더 혼란스러운 것 같아요....


추가적으로, 저는 개인적으로 인용되지 않은 전제에 대해 약간 회의적입니다(그러나 수정의 여지가 있습니다!). 다시 이전 게시물에서 언급했습니다.