Оператор NormalizeDouble работает с ошибками? - страница 4

 
VladAgapov >>:

Вообще-то, ни один опытный программист ни на одной платформе НИКОГДА не станет писать что-то вида:

1. Не используй прямое сравнение чисел с плавающей точкой.

2. Изучи и запомни особенности работы с ними.

3. Забудь про п.1

 
olvix >>:

Прошу прощения, что вмешиваюсь. Я новичок в програмировании (стаж создания простых программ около 3 лет). Подскажите я правильно понял? Если взять числа без запятой (7) произвести с ними операцию деления до 1или 2 (или 4) знака мы все равно полулучим в памяти число типа 0.699999999999999999 и 0.0699999999999999999999 соответственно (далее прербирать не буду) или все таки при использовании округления (норамализации) оно хотя бы после первой операции будет 0.7 и 0.07.


Вы поймите, в память компьютера невозможно записать число 0,7. Так как оно представлется в памяти в двоичном коде, то (условно говоря, я не буду сейчас переводить это число в двоичный код), есть варианты 0,69(9) и 0,70(1). Повторяю, это -- пример. Соответственно, условно говоря, нам надо решить, как записать результат в память. Мы его и записываем как 0,69(9). А потом мы произвели какие-то расчеты и получили 0,7000(3) его мы записали в память уже как 0,700(1) (ближайшее число из того, какое можно представить в двоичном коде, с заданной точностью хранения, которую мы используем). Сравнивай их не сравнивай, они никогда не будут равны. А нормализация -- такая же операция с числом. Число после нормализации не начинает записываться в память в десятичном виде. Результат всегда в двоичном виде. Нормализация приводит число в двоичном коде в другое его представление в двоичном коде, которое эквивалентно ближайшему округленному до нужной точности десятичному числу.

Так же возникает вопрос по обходу этой проблемы. Возникает ли этот эффект (т.е. представление числа 0.7 как 0.6999999999999) при использовании чтения из файла. Поясняю вопрос - если прочитать из текстового файла число 0.7 оно будет использоваться в советнике как 0.7 или как 0.6999999999999.

Оно будет выводиться на печать, в файл и везде как 0,7, но использоваться в расчетах будет 0,69(9). На результате вычислений это почти никак не скажется. =)

 
VladAgapov >>:

Но что можно сделать (и что нужно делать в таких случаях) - создать функцию-хелпер типа

bool IsDoubleEqual(double dVal1, double dVal2, int nDigits)

которая приводит числа dVal1 и dVal2 к точности nDigits, переводит их в целые числа и уж потом сравнивает.

Не совсем так. experts/libraries/stdlib.mq4, строка 145.

 
wise >>:
Вы поймите, в память компьютера невозможно записать число 0,7. Так как оно представлется в памяти в двоичном коде, то (условно говоря, я не буду сейчас переводить это число в двоичный код), есть варианты 0,69(9) и 0,70(1). Повторяю, это -- пример. Соответственно, условно говоря, нам надо решить, как записать результат в память. Мы его и записываем как 0,69(9). А потом мы произвели какие-то расчеты и получили 0,7000(3) его мы записали в память уже как 0,700(1) (ближайшее число из того, какое можно представить в двоичном коде, с заданной точностью хранения, которую мы используем). Сравнивай их не сравнивай, они никогда не будут равны. А нормализация -- такая же операция с числом. Число после нормализации не начинает записываться в память в десятичном виде. Результат всегда в двоичном виде. Нормализация приводит число в двоичном коде в другое его представление в двоичном коде, которое эквивалентно ближайшему округленному до нужной точности десятичному числу.

Оно будет выводиться на печать, в файл и везде как 0,7, но использоваться в расчетах будет 0,69(9). На результате вычислений это почти никак не скажется. =)

Большое спасибо за разъяснения. Вроде все просто, но именно из таких мелочей и складывается общая картина. Я этого не знал и рад, что теперь это понял (вычисления свои буду производить с оглядкой на это), осталось осознать и привыкнуть к этому положению вещей. 

 
VladAgapov писал(а) >>

Вы, к сожалению, не понимаете. Попробую пояснить (я новичок на этом форуме, но разработчик на разных языках, включая C++ и ранее assembler с 10-летним стажем).

ОС может хранить в памяти число 0.6999999999999999999999999, но не может - 0.7. Зато может, например, с любой точностью хранить 0.0000000000 :)

Это связано с тем, что числа с плавающей точкой в оперативной памяти хранятся лишь _в некотором приближении_, а не в абсолютных своих значениях.

К сожалению, это издержки цифрЫ вообще :) Вещественные числа по сути своей, "континуальны", а их представление в двоичном виде (да еще с ограничением по к-ву байт, необходимых для хранения числа) - "дискретно".

Самая проставя аналогия - фотография на пленку и цифровая фотография :) то же самое.

Спасибо, дошло. Всегда корректно сравнение только целых чисел.

 
wise писал(а) >>
Вы поймите, в память компьютера невозможно записать число 0,7. Так как оно представлется в памяти в двоичном коде, то (условно говоря, я не буду сейчас переводить это число в двоичный код), есть варианты 0,69(9) и 0,70(1). Повторяю, это -- пример. Соответственно, условно говоря, нам надо решить, как записать результат в память. Мы его и записываем как 0,69(9). А потом мы произвели какие-то расчеты и получили 0,7000(3) его мы записали в память уже как 0,700(1) (ближайшее число из того, какое можно представить в двоичном коде, с заданной точностью хранения, которую мы используем). Сравнивай их не сравнивай, они никогда не будут равны. А нормализация -- такая же операция с числом. Число после нормализации не начинает записываться в память в десятичном виде. Результат всегда в двоичном виде. Нормализация приводит число в двоичном коде в другое его представление в двоичном коде, которое эквивалентно ближайшему округленному до нужной точности десятичному числу.

Оно будет выводиться на печать, в файл и везде как 0,7, но использоваться в расчетах будет 0,69(9). На результате вычислений это почти никак не скажется. =)

Большое спасибо за ответ. Огромное спасибо всем, принявшим участие в этом, по-моему, полезном обсуждении. Тема исчерпана.

 
Кстати, а не пора ли уже создать какой-то раздел на форуме, эквивалентный FAQ?! И там публиковать ответы. Вопрос-то постоянно возникает.
 
dokpiknik >>:

И я не хотел высказываться по поводу некорректных советов, да, выскажусь. Многие программы пишутся на одном языке, только вот качество программирования разное. А вот сколько раз тема поднималась, меня не интересует. Есть простейшая встроенная функция, которая должна работать корректно, иначе ей грош цена. А время тратиь я предпочитаю не на разгребание чужих ошибок, а своих.

Дискуссия же на форуме может помочь разработчикам улучшить свой товар. Нормальный предприниматель критику воспринимает как большое благо. Так что о "гоне на разработчиков" с моей стороны не может быть и речи. А вот всеобщий "одобрямс", который до сих пор имеется в России и мешает ей достойно развиваться, на Западе как-то не в чести.

И последнее. Учусь я всегда охотно, даже если мне об этом не напоминают.

Все-таки дайте себе труд отделить мух от котлет.  

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

А к разработчикам есть очень много обоснованных претензий, в отличие от Вашей, и они их в меру возможности рихтуют ;).

Успехов в изучении мат.части. 

 
VladislavVG >>:

Все-таки дайте себе труд отделить мух от котлет.  

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

А к разработчикам есть очень много обоснованных претензий, в отличие от Вашей, и они их в меру возможности рихтуют ;).

Успехов в изучении мат.части. 

Обратите внимание, что для работы с финансовыми показателями применяются специальные библиотеки и специальные типы данных. Есть, например, тип decimal. При использовании этих типов и библиотек вышеописанной проблемы не возникает. Причем эти библиотеки были даже на заре компьютеризации, даже ещё на фортране были. Как это прошло мимо разработчиков МТ вполне понятно - наши программисты в массе самоучки, не знакомы с базовыми прикладными предметами, которые должны по хорошему преподаваться в ВУЗ-ах.

Успехов в изучении мат.части. 

Ага. И вам того же.

 
stringo писал(а) >>

После нормализации получается такое же вещественное число со всеми вытекающими. Не все дроби (а наши вещественные числа являются двоичными дробями) являются конечными, и разница между младшими разрядами может не быть единицей в соответствующей степени. Например, константа 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 специально предусмотрена, чтобы к брокеру поступали верные значения. Функция обязана давать точное значение! Уж не обижайтесь.