![MQL5 - Язык торговых стратегий для клиентского терминала MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
Вы упускаете торговые возможности:
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Регистрация
Вход
Вы принимаете политику сайта и условия использования
Если у вас нет учетной записи, зарегистрируйтесь
Вообще-то, ни один опытный программист ни на одной платформе НИКОГДА не станет писать что-то вида:
1. Не используй прямое сравнение чисел с плавающей точкой.
2. Изучи и запомни особенности работы с ними.
3. Забудь про п.1
Прошу прощения, что вмешиваюсь. Я новичок в програмировании (стаж создания простых программ около 3 лет). Подскажите я правильно понял? Если взять числа без запятой (7) произвести с ними операцию деления до 1или 2 (или 4) знака мы все равно полулучим в памяти число типа 0.699999999999999999 и 0.0699999999999999999999 соответственно (далее прербирать не буду) или все таки при использовании округления (норамализации) оно хотя бы после первой операции будет 0.7 и 0.07.
Так же возникает вопрос по обходу этой проблемы. Возникает ли этот эффект (т.е. представление числа 0.7 как 0.6999999999999) при использовании чтения из файла. Поясняю вопрос - если прочитать из текстового файла число 0.7 оно будет использоваться в советнике как 0.7 или как 0.6999999999999.
Оно будет выводиться на печать, в файл и везде как 0,7, но использоваться в расчетах будет 0,69(9). На результате вычислений это почти никак не скажется. =)
Но что можно сделать (и что нужно делать в таких случаях) - создать функцию-хелпер типа
bool IsDoubleEqual(double dVal1, double dVal2, int nDigits)
которая приводит числа dVal1 и dVal2 к точности nDigits, переводит их в целые числа и уж потом сравнивает.
Не совсем так. experts/libraries/stdlib.mq4, строка 145.
Вы поймите, в память компьютера невозможно записать число 0,7. Так как оно представлется в памяти в двоичном коде, то (условно говоря, я не буду сейчас переводить это число в двоичный код), есть варианты 0,69(9) и 0,70(1). Повторяю, это -- пример. Соответственно, условно говоря, нам надо решить, как записать результат в память. Мы его и записываем как 0,69(9). А потом мы произвели какие-то расчеты и получили 0,7000(3) его мы записали в память уже как 0,700(1) (ближайшее число из того, какое можно представить в двоичном коде, с заданной точностью хранения, которую мы используем). Сравнивай их не сравнивай, они никогда не будут равны. А нормализация -- такая же операция с числом. Число после нормализации не начинает записываться в память в десятичном виде. Результат всегда в двоичном виде. Нормализация приводит число в двоичном коде в другое его представление в двоичном коде, которое эквивалентно ближайшему округленному до нужной точности десятичному числу.
Оно будет выводиться на печать, в файл и везде как 0,7, но использоваться в расчетах будет 0,69(9). На результате вычислений это почти никак не скажется. =)
Большое спасибо за разъяснения. Вроде все просто, но именно из таких мелочей и складывается общая картина. Я этого не знал и рад, что теперь это понял (вычисления свои буду производить с оглядкой на это), осталось осознать и привыкнуть к этому положению вещей.
Вы, к сожалению, не понимаете. Попробую пояснить (я новичок на этом форуме, но разработчик на разных языках, включая C++ и ранее assembler с 10-летним стажем).
ОС может хранить в памяти число 0.6999999999999999999999999, но не может - 0.7. Зато может, например, с любой точностью хранить 0.0000000000 :)
Это связано с тем, что числа с плавающей точкой в оперативной памяти хранятся лишь _в некотором приближении_, а не в абсолютных своих значениях.
К сожалению, это издержки цифрЫ вообще :) Вещественные числа по сути своей, "континуальны", а их представление в двоичном виде (да еще с ограничением по к-ву байт, необходимых для хранения числа) - "дискретно".
Самая проставя аналогия - фотография на пленку и цифровая фотография :) то же самое.
Спасибо, дошло. Всегда корректно сравнение только целых чисел.
Вы поймите, в память компьютера невозможно записать число 0,7. Так как оно представлется в памяти в двоичном коде, то (условно говоря, я не буду сейчас переводить это число в двоичный код), есть варианты 0,69(9) и 0,70(1). Повторяю, это -- пример. Соответственно, условно говоря, нам надо решить, как записать результат в память. Мы его и записываем как 0,69(9). А потом мы произвели какие-то расчеты и получили 0,7000(3) его мы записали в память уже как 0,700(1) (ближайшее число из того, какое можно представить в двоичном коде, с заданной точностью хранения, которую мы используем). Сравнивай их не сравнивай, они никогда не будут равны. А нормализация -- такая же операция с числом. Число после нормализации не начинает записываться в память в десятичном виде. Результат всегда в двоичном виде. Нормализация приводит число в двоичном коде в другое его представление в двоичном коде, которое эквивалентно ближайшему округленному до нужной точности десятичному числу.
Оно будет выводиться на печать, в файл и везде как 0,7, но использоваться в расчетах будет 0,69(9). На результате вычислений это почти никак не скажется. =)
Большое спасибо за ответ. Огромное спасибо всем, принявшим участие в этом, по-моему, полезном обсуждении. Тема исчерпана.
И я не хотел высказываться по поводу некорректных советов, да, выскажусь. Многие программы пишутся на одном языке, только вот качество программирования разное. А вот сколько раз тема поднималась, меня не интересует. Есть простейшая встроенная функция, которая должна работать корректно, иначе ей грош цена. А время тратиь я предпочитаю не на разгребание чужих ошибок, а своих.
Дискуссия же на форуме может помочь разработчикам улучшить свой товар. Нормальный предприниматель критику воспринимает как большое благо. Так что о "гоне на разработчиков" с моей стороны не может быть и речи. А вот всеобщий "одобрямс", который до сих пор имеется в России и мешает ей достойно развиваться, на Западе как-то не в чести.
И последнее. Учусь я всегда охотно, даже если мне об этом не напоминают.
Все-таки дайте себе труд отделить мух от котлет.
Видите ли, способ машинного представления данных не зависит ни от языка программирования, ни от качества программирования.
А к разработчикам есть очень много обоснованных претензий, в отличие от Вашей, и они их в меру возможности рихтуют ;).
Успехов в изучении мат.части.
Все-таки дайте себе труд отделить мух от котлет.
Видите ли, способ машинного представления данных не зависит ни от языка программирования, ни от качества программирования.
А к разработчикам есть очень много обоснованных претензий, в отличие от Вашей, и они их в меру возможности рихтуют ;).
Успехов в изучении мат.части.
Обратите внимание, что для работы с финансовыми показателями применяются специальные библиотеки и специальные типы данных. Есть, например, тип decimal. При использовании этих типов и библиотек вышеописанной проблемы не возникает. Причем эти библиотеки были даже на заре компьютеризации, даже ещё на фортране были. Как это прошло мимо разработчиков МТ вполне понятно - наши программисты в массе самоучки, не знакомы с базовыми прикладными предметами, которые должны по хорошему преподаваться в ВУЗ-ах.
Успехов в изучении мат.части.
Ага. И вам того же.
После нормализации получается такое же вещественное число со всеми вытекающими. Не все дроби (а наши вещественные числа являются двоичными дробями) являются конечными, и разница между младшими разрядами может не быть единицей в соответствующей степени. Например, константа 0.7 после помещения в сопроцессор становится 0.6999999999999999. Нормализация вещественного числа производится несколькими операциями. В результате нормализации числа, например, 0.703 мы получим 0.7000000000000001. И всё! Два нормализованных числа уже не равны друг другу!
Поэтому при сравнении двух вещественных чисел (неважно, нормализованных или нет) мы должны принимать во внимание некую дельту. И эту дельту мы выбираем сами, исходя из наших потребностей.
Всё-таки ещё раз "к нашим баранам". Даю первую попавшуюся ссылку из Google по компьютерному представлению вещественных чисел http://kuzelenkov.narod.ru/mati/book/inform/inform5.html
Число 0.7 будет представлено в памяти компьютера точно в виде так называемого порядка - в нашем примере это 0 (или сдвинутого порядка) и мантиссы 7 (в двоичной форме 111). Остальные биты до конца разрядной сетки будут заполнены нулями. Другое дело, когда бесконечная дробь возникает в результате каких-нибудь операций: классический пример - деление 1 на 3. Возникает бесконечная периодическая дробь с периодом 3 и она естественно усекается и представляется в компьютере приближённо.
А мы-то речь ведём не об операциях, а о нормализации уже представленного в компьютере двоичного числа. Его требуется усечь, т.е использовать для его представления (точнее, представления его мантиссы) не всю разрядную сетку, а только её часть. Число безусловно будет представлено точно. Если библиотечная программа делает это иначе, её нужно поправить (чуть не сказал выбросить к чёрту!). И в случае корректной нормализации дробных чисел нет никаких проблем сравнивать их между собой.
Братцы, да как-же может быть иначе. Брокерские компьютеры принимают только нормализованные (значит, точные) значения, например, 1.3582, а функция NormalizeDouble специально предусмотрена, чтобы к брокеру поступали верные значения. Функция обязана давать точное значение! Уж не обижайтесь.