Попробуйте снять ограничение
И попробуйте сделать операторы сравнения "статическими".
Операторы не могут быть объявлены "статическими".
В одном операторе может быть более одной (перегрузочной) арифметической операции при условии, что каждый набор из двух операндов обернут в круглые скобки в правильном порядке. Я все еще не рекомендую это делать.
CDouble foo = 3, bar = 4, spam = 3; CDouble error = foo+bar+spam; //ERRORCDouble error_too = (pi2 + pi5)+pi2; //ERROR
CDouble correct = foo+(bar+spam);// OK!
Чтобы прояснить возможную путаницу, правильным способом обработки арифметики для операторов с более чем одним оператором является не использование перегруженных операторов, а применение одного из применимых методов получения значений.
CDouble sum, foo=3, bar=2, spam=1; sum = foo.AsRawDouble() + bar.AsRounded() + spam.AsRoundedTick(); Print(sum.ToString()); //6
Операторы не могут быть объявлены как "статические".
В одном операторе может быть более одной (перегрузочной) арифметической операции при условии, что каждый набор из двух операндов обернут в круглые скобки в правильном порядке. Я все еще не рекомендую это делать.
class CDouble2 : public CDouble { private: static CDouble2 TmpDouble; public: const CDouble2* const operator +( const double Value ) const { CDouble2::TmpDouble = this.m_value + Value; return(&CDouble2::TmpDouble); } const CDouble2* const operator +( const CDouble2 &Other ) const { return(this + Other.m_value); } static CDouble2* const Compare2( const double Value ) { CDouble2::TmpDouble = Value; return(&CDouble2::TmpDouble); } static CDouble2* const Compare2( const CDouble2 &Other ) { CDouble2::TmpDouble = Other; return(&CDouble2::TmpDouble); } }; static CDouble2 CDouble2::TmpDouble; #define _CP(A) CDouble2::Compare2(A) #define PRINT(A) Print(#A + " = " + (string)(A)); void OnStart() { CDouble2 foo = 3, bar = 4, spam = 3; CDouble2 error = foo+bar+spam + foo+bar+spam; //OK! PRINT(error.ToString()); // 10 PRINT(_CP(foo + error + 5) > 2); PRINT(_CP(25) > foo + bar + 7 +spam); PRINT((foo + bar + spam + 9).ToString()); PRINT((_CP(9) + foo).ToString()); PRINT(foo + 7 > 11) }
Результат
error.ToString() = 20 _CP(foo+error+5)>2 = true _CP(25)>foo+bar+7+spam = false (foo+bar+spam+9).ToString() = 19 (_CP(9)+foo).ToString() = 12 foo+7>11 = false
Результат
Это очень умно, и мне это очень нравится, но это слишком умно для большинства пользователей... (нас обоих обвиняли в этом на форумах ;) Я зафиксирую ваши изменения в своей личной библиотеке, и другие тоже могут, но для блага большей базы пользователей я собираюсь сохранить простоту и придерживаться официальной рекомендации вызывать один из методов геттера. ( например, num1.AsRounded() * num2.AsRounded() + num3.AsRounded() )
FWIW Мне лично нравится (num1*num2+num3).AsRounded()
Проблемы с CDouble2, как было предложено:
void Func(double param) { } void OnStart() { CDouble2 foo = 2, bar = 3; double number = foo+bar; //ERROR Func(foo+bar); //ERROR }
* Версия 1.01:
- Исправлена ошибка, из-за которой арифметические операторы не возвращали округленные значения.
- Добавлен метод symbol setter для установки символа после вызова конструктора
Здравствуйте, nicholishen. Я тестировал вашу библиотеку в течение некоторого времени. Она отличная и делает округление цен и партий легкой работой.
Но у меня есть некоторые опасения по поводу точности ваших методов округления. Я обнаружил множество ошибок округления в функциях RoundToStep(), RoundToStepUp(), RoundToStepDown() и RoundToTick(). Эти ошибки всегда возникают при округлении чисел, которые оканчиваются на 5 (например, 1.12345).
Например, CDouble::RoundToStep(1.700605, 0.00001) возвращает 1.70060, вместо правильного результата 1.70061.
Уравнение round(number / point) * point должно быть исправлено на round(number * power) / power, где и point, и power являются производными от количества десятичных цифр, до которых вы хотите округлить.
Потому что значение 1 пункта, которое должно быть = 0.00001, на самом деле кодируется как 0.0000100000000000000008180305391403130954586231382563710 в 64-битной системе с плавающей точкой двойной точности. Это приводит к тому, что конечный результат, полученный методом округления round(number / point) * point, очень часто отклоняется от правильного результата на 1 пункт (0.00001).
Кроме того, чтобы выполнить правильное "арифметическое" округление (округление средней точки от нуля), хорошим методом является добавление или вычитание полуэпсилона в качестве поправки. (Это компенсирует любое округление от половины до четности, которое было применено процессором, как предписано спецификациями IEEE-754, особенно в крайних случаях средней точки).
Функция mql's NormalizeDouble() корректно решает все эти вопросы, и вам следует использовать ее для выполнения правильного "арифметического" округления.
Здесь также приведен исходный код одной функции, которую я написал для выполнения арифметического округления, и вы можете проверить ее самостоятельно. Эта функция дает точно такие же результаты, как и NormalizeDouble(). Моя функция работает даже быстрее и поддерживает более высокий уровень точности округления. (В MQL функция NormalizeDouble() ограничена 8 десятичными цифрами).
/** * Округление средней точки от нуля ("арифметическое" округление) * Использует полуэпсилон для коррекции. (Это компенсирует IEEE-754 * округление от половины до четности, которое было применено в крайних случаях). */ double RoundCorrect(double num, int precision) { double c = 0.5 * DBL_EPSILON * num; // double p = MathPow(10, precision); //slow double p = 1; while (precision--> 0) p *= 10; if (num < 0) p *= -1; return MathRound((num + c) * p) / p; }
Кроме того, вот скрипт, который можно использовать для отладки точности округления в библиотеке CDouble. Надеюсь, вы найдете его полезным для себя.
#property strict #define PRINT(A) Print(#A + " = ", (A)) #define forEach(element, array) for (int __i = 0, __max = ArraySize((array)); __i < __max && ((element) = array[__i]) == (element); __i++) #include "CDouble.mqh" //+------------------------------------------------------------------+ //|| //+------------------------------------------------------------------+ string DoubleToFixed(double number, int decimals = 55) { return StringFormat(StringFormat("%%#.%if", decimals), number); } //+------------------------------------------------------------------+ //|| //+------------------------------------------------------------------+ void OnStart() { // проверка округления в некоторых крайних случаях double numbers_3[] = {1.005, 2.175, 5.015, 16.025}; double numbers_6[] = {1.011885, 1.113325, 1.143355, 1.700605}; double num; forEach (num, numbers_3) { Print("----------------------------------------------"); PRINT( num ); // сравните 3 функции (округлите до 2 цифр) PRINT( CDouble::RoundToStep(num, 0.01) ); PRINT( NormalizeDouble(num, 2) ); PRINT( RoundCorrect(num, 2) ); } forEach (num, numbers_6) { Print("----------------------------------------------"); PRINT( num ); // сравните 3 функции (округлите до 5 цифр) PRINT( CDouble::RoundToStep(num, 0.00001) ); PRINT( NormalizeDouble(num, 5) ); PRINT( RoundCorrect(num, 5) ); } // Причина проблем с округлением в библиотеке CDouble Print("----------------------------------------------"); PRINT( DoubleToFixed(0.01, 55) ); // 0.0000100000000000000008180305391403130954586231382563710 PRINT( DoubleToFixed(0.00001, 55) ); // 0.0100000000000000002081668171172168513294309377670288086 // сравните NormalizeDouble и RoundCorrect на основе точного равенства Print("----------------------------------------------"); PRINT( NormalizeDouble(numbers_6[0], 5) == RoundCorrect(numbers_6[0], 5) ); // true PRINT( NormalizeDouble(numbers_6[0], 4) == RoundCorrect(numbers_6[0], 4) ); // true PRINT( NormalizeDouble(numbers_6[0], 3) == RoundCorrect(numbers_6[0], 3) ); // true PRINT( NormalizeDouble(numbers_6[0], 2) == RoundCorrect(numbers_6[0], 2) ); // true PRINT( NormalizeDouble(numbers_6[0], 1) == RoundCorrect(numbers_6[0], 1) ); // true }
Здравствуйте, nicholishen. Я тестировал вашу библиотеку в течение некоторого времени. Она отличная и делает округление цен и партий легкой работой.
Но у меня есть некоторые опасения по поводу точности ваших методов округления. Я обнаружил множество ошибок округления в функциях RoundToStep(), RoundToStepUp(), RoundToStepDown() и RoundToTick(). Эти ошибки всегда возникают на крайних числах, которые оканчиваются на 5 (например, 1.12345).
Например, CDouble::RoundToStep(1.700605, 0.00001) возвращает 1.70060, вместо правильного результата 1.70061.
Уравнение round(number / point) * point должно быть исправлено на round(number * power) / power, где и point, и power являются производными от количества десятичных цифр, до которых вы хотите округлить.
Потому что значение 1 пункта, которое должно быть = 0.00001, на самом деле кодируется как 0.0000100000000000000008180305391403130954586231382563710 в 64-битной системе с плавающей точкой двойной точности. Это приводит к тому, что конечный результат, полученный методом округления round(number / point) * point, очень часто отклоняется от правильного результата на 1 пункт (0.00001).
Кроме того, чтобы выполнить правильное "арифметическое" округление (округление средней точки от нуля), хорошим методом является добавление или вычитание полуэпсилона в качестве поправки. (Это компенсирует любое округление от половины до четности, которое было применено процессором, как предписано спецификациями IEEE-754, особенно в крайних случаях средней точки).
Функция mql's NormalizeDouble() корректно решает все эти вопросы, и вам следует использовать ее для выполнения правильного "арифметического" округления.
Здесь также приведен исходный код одной функции, которую я написал для выполнения арифметического округления, и вы можете проверить ее самостоятельно. Эта функция дает точно такие же результаты, как и NormalizeDouble(). Моя функция работает даже быстрее и поддерживает более высокий уровень точности округления. (В MQL функция NormalizeDouble() ограничена 8 десятичными цифрами).
Спасибо, что указали на это. Я обновлю код, чтобы использовать NormalizeDouble вместо round.
step * NormalizeDouble(number_to_round / step, 0)
Здравствуйте, nicholishen. Я тестировал вашу библиотеку в течение некоторого времени. Она отличная и делает округление цен и партий легкой работой.
Но у меня есть некоторые опасения по поводу точности ваших методов округления. Я обнаружил множество ошибок округления в функциях RoundToStep(), RoundToStepUp(), RoundToStepDown() и RoundToTick(). Эти ошибки всегда возникают на крайних числах, которые оканчиваются на 5 (например, 1.12345).
Например, CDouble::RoundToStep(1.700605, 0.00001) возвращает 1.70060, вместо правильного результата 1.70061
Уравнение round(number / point) * point должно быть исправлено на round(number * power) / power, где и point, и power являются производными от количества десятичных цифр, до которых вы хотите округлить.
Потому что значение 1 пункта, которое должно быть = 0.00001, на самом деле кодируется как 0.0000100000000000000008180305391403130954586231382563710 в 64-битной системе с плавающей точкой двойной точности. Это приводит к тому, что конечный результат от метода округления, round(number / point) * point, очень часто отклоняется от правильного результата на 1 пункт (0.00001).
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
CDouble & CDoubleVector:
Библиотека для проведения общих методов округления, используемых в разработке MQL-приложений, примитивный класс-оболочка для значений типа double и вектор для объектов CDouble. Совместима с MQL5 и MQL4!
Версия 1.02: (2018.02.18)
Версия 1.01:
Автор: nicholi shen