尝试删除约束
并尝试使比较运算符成为 "静态 "运算符。
操作符不能声明为 "静态"。
每条语句可以有一个以上的(重载)算术运算,条件是每组两个操作数都按正确的顺序用括号包装起来。我仍然不建议这样做。
CDouble foo = 3, bar = 4, spam = 3; CDouble error = foo+bar+spam; //ERRORCDouble error_too = (pi2 + pi5)+pi2; //ERROR
CDouble correct = foo+(bar+spam);// 确定!
运算符不能声明为 "静态"。
每条语句可以有一个以上的(重载)算术运算,但每组两个操作数必须按正确的顺序用括号包装起来。我仍然不建议这样做。
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() )
我个人喜欢 (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:
- 修正了算术运算符不能返回四舍五入值的错误。
- 添加了符号设置方法,以便在调用构造函数后设置符号
你好,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 的NormalizeDouble() 函数能 正确处理所有这些问题,你应该使用它来进行正确的 "算术 "四舍五入。
这里还有我写的一个算术四舍五入函数的源代码,你可以自己测试。该函数的结果与 NormalizeDouble() 完全相同。我的函数运行速度更快,并且支持更高水平的四舍五入精度。(MQL 的 NormalizeDouble() 仅限于小数点后 8 位)。
/** * MidpointRounding 远离零("算术 "四舍五入) * 使用半epsilon 进行修正。(这抵消了在边缘情况下应用的 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 个函数(四舍五入到两位数) 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 的NormalizeDouble() 函数可以 正确处理所有这些问题,您应该使用它来进行正确的 "算术 "四舍五入。
这里还有我写的一个算术四舍五入函数的源代码,你可以自己测试。该函数的结果与 NormalizeDouble() 完全相同。我的函数运行速度更快,支持更高水平的四舍五入精度。(MQL 的 NormalizeDouble() 仅限于小数点后 8 位)。
感谢您指出这一点。我将更新代码,使用 NormalizeDouble 代替四舍五入。
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)。
CDouble & CDoubleVector:
用于 MQL 开发的常用舍入方法函数库,用于类型 (double) 的元初包装类和 CDouble 对象的向量。 MQL5 和 MQL4 兼容!
用于 MQL 开发的常用舍入方法函数库,用于类型 (double) 的元初包装类和 CDouble 对象的向量。 MQL5 和 MQL4 兼容!
版本 1.02: (2018.02.18)
版本 1.01:
作者: nicholi shen