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

 
prostotrader:

Утро вечера мудреннее... :)

 

Обычно так и есть :)
 
В СД говорят, что все правильно в этом случае

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Ошибки, баги, вопросы

fxsaber, 2017.07.18 09:51

Почти детский вопрос: почему так?
void OnStart()
{
  const double Norm = NormalizeDouble(8905 / 1000.0, 3);
  Print(Norm); // 8.904999999999999
  Print(DoubleToString(Norm, 3)); // 8.905
  
  const double Norm2 = (double)DoubleToString(Norm, 3);
  Print(Norm2); // 8.904999999999999
  Print(Norm == Norm2); // true
}

По какой-то причине был уверен, что после нормализации DoubleToString лишен смысла. Ан нет, как показывает скрипт. Почему так?

Похоже, что неправильно работает приведение double -> string.


Пример довольно сложен для понимания, поэтому привел другой

void OnStart()
{
  Print((double)"8.905" == 8.905); // true
  Print(((string)(double)"8.905")); // 8.904999999999999
}

Речь вот о чем

Строковая в double переводится без проблем. Обратно - получаем другой результат.

Т.е. взял строку "8.905", преобразовал ее в double и сразу в string, получив 8.904999999999999. Но первая строка OnStart показывает, что (double)"8.905" == 8.905. Т.е. должно было выводиться 8.905.

Конечно, очевидная ситуация с нулем на конце не должна работать:

Если выполняется условие (double)"8.9050" == 8.9050, то должно выполняться и условие (string)(double)"8.9050" == "8.9050".

Немного исследовав вопрос, пришел к такой ситуации

void OnStart()
{
  const double Num = 8.274;
  const double Norm = NormalizeDouble(Num, 3);
  
  Print(Num);  // 8.273999999999999
  Print(Norm); // 8.274000000000001
}

Прошу объяснить, почему все равно считается, что преобразование double -> string происходит корректно. Последний пример так совсем выносит мозг.

 
fxsaber:

Прошу объяснить, почему все равно считается, что преобразование double -> string происходит корректно. Последний пример так совсем выносит мозг.

Комментарий по последнему примеру

Вещественные числа могут считаться одинаковыми, если над ними производили одни и те же преобразования. Даже вроде бы одинаковые преобразования - num*0.5 и num/2.0 приводят к разным результатам. То же самое можно сказать про зеркальные операции. num*=num2, num/=num2. Полученный num не будет равен исходному num.  Добро пожаловать в мир вещественных чисел.

В данном примере в процессе нормализации с вещественным числом были произведены 3 операции - num*=1000, num+=0.5, num/=1000

Можете проверить по шагам в отладчике

 
fxsaber:
В СД говорят, что все правильно в этом случае

А почему смущает?

Подавляющее большинство десятичных вещественных чисел непредставимы в виде двоичной дроби без остатка. Накладываем на это формат хранения числе double и получаем такие некрасивости.

Вообще тип decimal не помешал бы, удобная штука.

 
Slava:

Комментарий по последнему примеру

Вещественные числа могут считаться одинаковыми, если над ними производили одни и те же преобразования. Даже вроде бы одинаковые преобразования - num*0.5 и num/2.0 приводят к разным результатам. То же самое можно сказать про зеркальные операции. num*=num2, num/=num2. Полученный num не будет равен исходному num.  Добро пожаловать в мир вещественных чисел.

В данном примере в процессе нормализации с вещественным числом были произведены 3 операции - num*=1000, num+=0.5, num/=1000

Можете проверить по шагам в отладчике


Очень конструктивное объяснение, Спасибо!


Но вот такой контрпример выносит несколько мозг

void OnStart()
{
  const double Num = 8.274;
  const double Norm = NormalizeDouble(Num, 3);
   
  Print(Num);  // 8.273999999999999
  Print(Norm); // 8.274000000000001
  
  Print((double)DoubleToString(Num, 3) == Num);     // true - без нормализации все замечательно
  Print((double)DoubleToString(Norm, 3) == Norm);   // false - а после нормализации полный облом!
}

Разве должна так работать нормализация?

 
Комбинатор:

А почему смущает?

После объяснения Славы уже не смущает, но далее возник пример, который заставляет усомниться в правильности работы теперь уже самой NormalizeDouble.

 
Комбинатор:

Вообще тип decimal не помешал бы, удобная штука.

Да, его отсутствие в софте, который работает с ценами, с самого начала существования МТ, мягко говоря, смущает.

PS. Теперь, при наличии ООП языка, MQ наверно считают, что желающие могут себе класс написать. Только его потом в простую структуру не положишь - нужно будет сериализовать/десериализовать во что-то простое типа ulong.
 
Slava:

Я в самом деле очень благодарен Вам, что столь подробно отвечаете. Нормализация используется для формирования торговых запросов.

// Point = 0.001, Digits = 3
OrderSend(8274 * Point);
OrderSend(NormalizeDouble(8274 * Point, Digits));

В этом примере окажется, что отправляются разные цены в двух этих OrderSend.

При этом всегда считалось, что умножение целого на Point не требует доп. нормализации (так задавали SL и TP, например).

Так какая из двух строк вызовет ошибку?

 
Stanislav Korotky:

Да, его отсутствие в софте, который работает с ценами, с самого начала существования МТ, мягко говоря, смущает.

Не может быть чтобы в СД никто не писал
 
fxsaber:

Я в самом деле очень благодарен Вам, что столь подробно отвечаете. Нормализация используется для формирования торговых запросов.

В этом примере окажется, что отправляются разные цены в двух этих OrderSend.

При этом всегда считалось, что умножение целого на Point не требует доп. нормализации (так задавали SL и TP, например).

Так какая из двух строк вызовет ошибку?

Прикольно

#include <MT4Orders.mqh>

void OnStart()
{
  const double Num = 8.274;
  const double Norm = NormalizeDouble(Num, 3);  
   
  Print(Num);  // 8.273999999999999
  Print(Norm); // 8.274000000000001
  
  Print((double)DoubleToString(Num, 3) == Num);     // true - без нормализации все замечательно
  Print((double)DoubleToString(Norm, 3) == Norm);   // false - а после нормализации полный облом!
  
  OrderSend("USDSEK", OP_BUYLIMIT, 1, Num, 0, 0, 0);
  OrderSend("USDSEK", OP_BUYLIMIT, 1, Norm, 0, 0, 0);
}

Результат

script Test (EURUSD,M1) loaded successfully
'6185283': buy limit 1.00 USDSEK at 8.27400
'6185283': accepted buy limit 1.00 USDSEK at 8.27400
'6185283': order #158260308 buy limit 1.00 / 1.00 USDSEK at market done in 98.718 ms
'6185283': buy limit 1.00 USDSEK at 8.27400
'6185283': accepted buy limit 1.00 USDSEK at 8.27400
'6185283': order #158260309 buy limit 1.00 / 1.00 USDSEK at market done in 120.328 ms
script Test (EURUSD,M1) removed

Оба запроса с разными ценами, но выполнились без проблем по одной и той же цене. Как так?