Ошибки, баги, вопросы - страница 2821

 
Alexey Navoykov:
Да вроде тут дело не в функции, а в том, что константы не нормализуются компилятором (хотя следовало бы).
Это же касается и преобразования string в double.

Тогда одни те же константы в DLL и MQL будут не совпадать.

 
fxsaber:

Тогда одни те же константы в DLL и MQL будут не совпадать.

Тоже верно. Да и к тому же любая нормализация - это потеря точности, поэтому я пожалуй погорячился с нормализацией констант.
В общем данная проблема, на мой взгляд, не имеет однозначного решения.  Хотя по сути это не такая уж и проблема.
 
Alexey Navoykov:
Тоже верно. Да и к тому же любая нормализация - это потеря точности, поэтому я пожалуй погорячился с нормализацией констант.
В общем данная проблема, на мой взгляд, не имеет однозначного решения.  Хотя по сути это не такая уж и проблема.

Просто подправить текущий алгоритм нормализации.

 
fxsaber:

Просто подправить текущий алгоритм нормализации.

void OnStart() {
   double d1=1.79435;
   double d2=NormalizeDouble(1.79435,5);
   Print(d1==d2);
   Print(is_equal(d1,d2,_Point/2));
}
//+------------------------------------------------------------------+
bool is_equal(double d1, double d2, double e=0.000000001) {return fabs(d1-d2)<e;}

даже не знаю, является ли это ошибкой алгоритма.
Действительно, нельзя сравнивать double. Просто жёсткое правило.
Или, как говорит Слава, через эпсилон или через умножение (например на 1/_Point) с преобразованием в int c округлением.

Только округление не через штатные round(), ceil(), floor() т.к. они тоже возвращают double.

А через эти, тем более они работают быстрее штатных:

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;}
 
Nikolai Semko:

Только округление не через штатные round(), ceil(), floor() т.к. они тоже возвращают double.

А через эти, тем более они работают быстрее штатных:

Может и быстрее, да только неправильнее.
Передадите в свой ceil что-нибудь вроде 12345.0000000000001 (аналогично вашему примеру), и на выходе можете получить 12346
 
Alexey Navoykov:
Может и быстрее, да только неправильнее.
Передадите в свой ceil что-нибудь вроде 12345.0000000000001 (аналогично вашему примеру), и на выходе можете получить 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, т.к. значимых цифр в типе double 17, а у вас 18
 
Nikolai Semko:

Действительно, нельзя сравнивать double. Просто жёсткое правило.

Конечно, можно и иногда даже нужно сравнивать double напрямую между собой.

Например, при Оптимизации OnTick вызывается иногда триллион раз. Штатный Тестер для того, чтобы понять, исполнять висящий лимитник или нет, делает сравнение текущей соответствующей цены символа и цены лимитника. Делает это для каждой отложки перед каждым OnTick вызовом. Т.е. таких проверок делается десятки и сотни миллиардов раз.

И делается это каждый раз через нормализацию. Так вот - это жуткое расходование вычислительных ресурсов впустую. Т.к. цены и отложенных ордеров и символа предварительно нормализованы. Поэтому могут и должны сравниваться между собой напрямую.

Не на ровном месте MQL-кастомный Тестер уделывает нативный штатный Тестер по производительности.

 

fxsaber
:

Конечно, можно и иногда даже нужно сравнивать double напрямую между собой.

Например, при Оптимизации OnTick вызывается иногда триллион раз. Штатный Тестер для того, чтобы понять, исполнять висящий лимитник или нет, делает сравнение текущей соответствующей цены символа и цены лимитника. Делает это для каждой отложки перед каждым OnTick вызовом. Т.е. таких проверок делается десятки и сотни миллиардов раз.

И делается это каждый раз через нормализацию. Так вот - это жуткое расходование вычислительных ресурсов впустую. Т.к. цены и отложенных ордеров и символа предварительно нормализованы. Поэтому могут и должны сравниваться между собой напрямую.

Не на ровном месте MQL-кастомный Тестер уделывает нативный штатный Тестер по производительности.

NormalizeDouble() очень дорогая функция. Поэтому про нее лучше забыть.

Вот скрипт, который демонстрирует разницу между NormalizeDouble() и нормализацию с помощью 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.5046099795727060
2020.08.10 12:55:30.766 TestSpeedNormalizeDouble (USDCAD,H4)    сумма, нормализированная через int       - 1733 микросекунд, сумма = 626010.5046099999453873
ЗЫ причем нормализация через int получается еще и точнее (это можно увидеть по количеству девяток после последней цифры нормализации - выделено синим.)
 
Nikolai Semko:

NormalizeDouble() очень дорогая функция. Поэтому про нее лучше забыть.

Вот скрипт, который демонстрирует разницу между NormalizeDouble() и нормализацию с помощью int:

результат:

ЗЫ причем нормализация через int получается еще и точнее (это можно увидеть по количеству девяток после последней цифры нормализации - выделено синим.)

а если суммировать не через double, а через long, тогда результат еще более впечатляет, т.к. суммирование через int (умножение и округление с последующим делением итоговой суммы) вычесляется быстрее обычной суммы double.

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

результат:

Decimal для сравнения добавьте.

Ошибся ссылкой, там не полная реализация.