Может ли цена != цена ?

 

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

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

По сути, это похоже на правду...

double TestValue = iClose(NULL, 0, 0);
   
if(TestValue != NormalizeDouble(TestValue, Digits) )

... есть идеи, как это происходит?

 
Предыдущие вопросы и ответы
 
RaptorUK:

. ... есть идеи, как это происходит?

Это связано с внутренней работой функции NormalizeDouble(). Например...

   double TestValue = 1.57373;
   if (TestValue != NormalizeDouble(TestValue, 5)) MessageBox("WTF?");

Кстати, те же результаты получаются, если сделать следующее:

   double TestValue = StrToDouble("1.57373");
   if (TestValue != NormalizeDouble(TestValue, 5)) MessageBox("WTF?");

После первоначального присвоения TestValue = 1.5737300000000001. NormalizeDouble(..., 5) выдает 1.5737299999999999.

 
WHRoeder:
Предыдущие вопросы и ответы
При всем уважении, я не думаю, что это сообщение отвечает на мой вопрос в этой теме.
Я знаю, что есть другие способы сделать это, кроме NormalizeDouble ... что я не понимаю, так это почему iClose возвращает значение, которое еще не нормализовано ...
 
jjc:

Это связано с внутренней работой функции NormalizeDouble(). Например...

Кстати, те же результаты получаются, если сделать следующее:

После первоначального присвоения TestValue = 1.5737300000000001. NormalizeDouble(..., 5) выдает 1.5737299999999999.


Как же мне заставить TestValue быть равным 1.57373 не > или <?
 
RaptorUK:

Итак, как сделать так, чтобы TestValue было равно 1,57373, а не > или <?
Если это еще не ясно, то 1.57373 не может быть представлено точно как значение с плавающей точкой. То же самое справедливо и для таких значений, как 0,1. Единственная странность заключается в том, что NormalizeDouble() в конечном итоге использует другое приближение, чем другие части языка MQ4.
 
jjc:
Если это еще не ясно, то 1,57373 не может быть представлено точно как значение с плавающей точкой. То же самое справедливо и для таких значений, как 0,1. Единственная странность заключается в том, что функция NormalizeDouble() использует другое приближение, чем другие части языка MQ4.

Ах ... нет, это было неясно ... Я не знал этого. Спасибо, я изучу вопрос.
 
RaptorUK:
Ах ... нет, это было неясно ... Я не знал этого. Спасибо, я изучу вопрос.

Значения с плавающей точкой и арифметика быстры, потому что их поддержка встроена в процессор компьютера, но с тем компромиссом, что некоторые значения не могут быть точно представлены в переменной с плавающей точкой. (Пример последствий для скорости см. на сайте https://www.mql5.com/en/forum/116228/page2#156859).

По сути, все, что связано с удвоениями, может привести к ошибке округления. Это приводит к разного рода забавным причудам. Например, 0,1 * 10 = 1,0, но 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 = 1,0.

В результате получается, что NormalizeDouble(x, y) не совсем синоним Round(x, y). NormalizeDouble() возвращает наиболее близкое к округленному значению приближение с плавающей точкой. Если вы делаете NormalizeDouble(a, n) == NormalizeDouble(b, n), то вы, по сути, говорите: "равны ли a и b с учетом того, что арифметика с плавающей запятой может вносить различия в округление более чем на n десятичных знаков?".

Как уже многие говорили, NormalizeDouble(a, 5) == NormalizeDouble(b, 5) по сути эквивалентно MathAbs(a - b) < 0.00001, и последнее выполняется немного быстрее. Последний вариант также распространен, потому что он широко используется в языках/платформах, которые не предоставляют удобного эквивалента функции NormalizeDouble(). Но разница в производительности настолько ничтожна, что я бы придерживался NormalizeDouble(), если вы чувствуете, что это делает ваш код более читабельным.

Все это совершенно нормально для языков, в которых есть тип данных double. Единственное, что вносит некоторую собственную и типичную для MQ4 причудливость, это то, что 1.57373 != NormalizeDouble(1.57373, 5). Это извращение, что объявление константы 1.57373 и использование NormalizeDouble() выбирают разные наилучшие приближения значения с плавающей точкой.

 

Спасибо. :-)

Я знал о проблеме, но не совсем понимал причину и, следовательно, не полностью осознавал возможные последствия.

 
за исключением нуля, никогда не сравнивайте двойники на равенство
if (a > b)
if (a - b > Point / 2.)
if (a >= b)
if (a - b > -Point)
if (a != b)
if (MathAbs(a - b) > Point / 2.)
 
WHRoeder:
кроме как с нулем никогда не сравнивать двойные числа на равенство


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

В недалеком будущем я буду модифицировать этот блок кода, чтобы заставить его работать с таймфреймами, отличными от таймфрейма графика, на котором он запущен. Когда я приду к этому, я планирую удалить NormalizeDoubles и заменить его чем-то другим... пока не уверен на 100%, возможно, преобразованием в целые числа перед сравнением...

Спасибо за помощь, как обычно :-)

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