Оператор NormalizeDouble работает с ошибками?

 

Похоже, что оператор NormalizeDouble не всегда корректно выполняет свою функцию. Долгое время я не мог найти причину того, почему иногда при попытке модифицировать ордер я получал сообщение об ошибке.

Вновь и вновь я делал тщательную проверку алгоритма и не находил ошибок. Мистика! Только тогда я поставил под сомнение правильность отработки указанного оператора и заменил его простой функцией:

MathRound(number/Point)*Point, где number - ненормализованная цена.

Ошибочные сообщения как рукой сняло! Что бы это значило?

 

Продемонстрируйте то самое место в сравнении NormlizeDouble() c MathRound(number/Point)*Point. Интересно, в чем же ошибка NormlizeDouble()?

 
Integer писал(а) >>

Продемонстрируйте то самое место в сравнении NormlizeDouble() c MathRound(number/Point)*Point. Интересно, в чем же ошибка NormlizeDouble()?

Демонстрирую:

В старом "ошибочном" варианте я использовал оператор

NormalizeDouble(number, Digits)

А вот почему замена одной конструкции другой при сохранении ВСЕГО остального программного окружения без изменения привела к исчезновению сообщений об ошибке (OrderModify выполняется безукоризненно) мне бы самому хотелось знать. Между тем из всего случившегося вытекает (не очевидный, но очень навязчивый) вывод, что NormalizeDouble работает не нормально. "Не очевидный" потому, что, несмотря на оставшееся без изменения МОЁ программное окружение, замена конструкций может теоретически вызвать неадекватную (или, наоборот, адекватную) реакцию нестабильных (а они как правило имеются в любой даже очень совершенной системе) СИСТЕМНЫХ (МТ4) программных средств.

 
Integer писал(а) >>

Продемонстрируйте то самое место в сравнении NormlizeDouble() c MathRound(number/Point)*Point. Интересно, в чем же ошибка NormlizeDouble()?

Вот фрагмент цикла (оригинал), где всё происходит. Жирным выделена изменённая конструкция, модифицирующий оператор и оператор, сообщающий об ошибке при попытке модификации ордера. Имена переменных понятны, надеюсь, из контекста.

...
op_obj = ObjectGet(obj_order[i], OBJPROP_PRICE1);
if(op[i] != op_obj)
{
op_obj = MathRound(op_obj/Point)*Point;
ObjectDelete(obj_order[i]);
if(op[i] == op_obj)
{
ObjectCreate(obj_order[i], OBJ_HLINE, 0, 0, op[i]);
ObjectSet(obj_order[i], OBJPROP_STYLE, STYLE_SOLID);
ObjectSet(obj_order[i], OBJPROP_COLOR, Gray);
}
else
{
if(type[i] == OP_SELLSTOP)
{
if(op[i]+stop_level < Bid)
{
if(op_obj+stop_level >= Bid) op_obj = Bid-stop_level;
sl_obj = sl[i] + (op_obj - op[i]);
tp_obj = tp[i] + (op_obj - op[i]);

if(OrderModify(ticket[i], op_obj, sl_obj, tp_obj, 0))
{
op[i] = op_obj;
sl[i] = sl_obj;
tp[i] = tp_obj;
}
else
{
fox_error(GetLastError());
}
ObjectCreate(obj_order[i], OBJ_HLINE, 0, 0, op[i]);
ObjectSet(obj_order[i], OBJPROP_STYLE, STYLE_SOLID);
ObjectSet(obj_order[i], OBJPROP_COLOR, Gray);
}
else
{
...

 

Из-за особенностей нормализации может быть ?

http://www.metatrader4.com/ru/forum/12793

 
dokpiknik писал(а) >>

Вот фрагмент цикла (оригинал.........

К сожалению ничего не увидел. Выведите через Print(), что было до нормализации, что было после, и сравните с вашим вариантом, также до и после. Выводить 8 знаков: Print(DoubleToStr( переменная,8)).

 

Вообще говоря, все эти поинты, так красиво выглядящие в десятичной системе, в двоичной системе представляются только бесконечными дробями. Поэтому все операции, кажущиеся круглыми, приводят к потере точности последнем двоичном знаке, и легко можно столкнуться с тем, что, например, 0.3/3 совсем не равно 0.1, как следовало бы ожидать. Желающие, могут проверить, запустив следующий, предельно простой код:

void start () {
if ( 0.3/3 == 0.1 ) Alert ("Equal");
else Alert ("Not equal");
}

По этой дурацкой причине, я НИКОГДА не использую операции сравнения на равенство/неравенство (а также >=, <= и с оговорками – <, >) двух чисел с плавающей точкой.

У NormalizeDouble и у MathRound разные внутренние алгоритмы работы, которые, скорей всего, приводят к разнице в последних двоичных знаках.

 
Я бы не стал называть это ни багом, ни особенностью MT4. Это является неким фундаментальным фактором, не зависящим ни от платформы, ни от языка программирования. Единственный способ борьбы с этим явлением – введение малой дельты и проверка на попадание одной величины в диапазон плюс/минус дельта от другой величины. Противно, но ничего не поделаешь.
 
Shaitan писал(а) >>

Вообще говоря, все эти поинты, так красиво выглядящие в десятичной системе, в двоичной системе представляются только бесконечными дробями. Поэтому все операции, кажущиеся круглыми, приводят к потере точности последнем двоичном знаке, и легко можно столкнуться с тем, что, например, 0.3/3 совсем не равно 0.1, как следовало бы ожидать.

По этой дурацкой причине, я НИКОГДА не использую операции сравнения на равенство/неравенство (а также >=, <= и с оговорками – <, >) двух чисел с плавающей точкой.

У NormalizeDouble и у MathRound разные внутренние алгоритмы работы, которые, скорей всего, приводят к разнице в последних двоичных знаках.

Полностью согласен с Вами. Между тем, думаю, что алгоритм сравнения в системных программах может быть усовершенствован. Уверен в этом. Microsoft, например, не позволяет себе подобной неряшливости. Да, никто это долго и терпеть не стал бы.

Ошибка происходит, грубо говоря, где-то на уровне +0/-0, хотя математически значение одно и то же. При использовании моего нового алгоритма происходит переход к целым числам, где ошибок "не бывает", а затем отсекается дробная часть.

 
dokpiknik писал(а) >>

При использовании моего нового алгоритма происходит переход к целым числам, где ошибок "не бывает", а затем отсекается дробная часть.

Увы и ах! С вашим методом нормализации возможна ошибка на уровне +-0, и она случается. Проверено.

 
Integer писал(а) >>

Увы и ах! С вашим методом нормализации возможна ошибка на уровне +-0, и она случается. Проверено.

К сожалению, я установил то же самое. Выходит ошибка происходит во время сравнения дробных (double) нормализованных ("усечённых") чисел. Таким образом, остаётся одно: дробные числа превращать в целые (разделив их на Point) и равнивать уже их между собой. Уж в этом-то случае (при сравнении целых чисел) должно быть всё тип-топ! Или та же проблема с +0/-0 может возникнуть? Однако, всё это достаточно неудобно...

Причина обращения: