在赫兹量化中处理双精度浮点数
简介
赫兹量化编程为自动化交易提供了新的机遇,世界各地的众多人士已对其表示赞赏。
当我们编写用于交易的 Expert Advisor 时,必须确保其运行无误。
很多新手经常在一些数学计算的结果跟预期结果不一致时产生困惑。 程序可以编译和工作,但跟预期并不一致。 他们反复检查代码,试图在语言、实现和函数等方面找出新的“错误”。
多数情况下,经过仔细分析后会发现语言和编译工作良好,但代码有小错误,找出并改正需要花费大量的时间。
在本文中我们将研究典型的编程错误,这种错误在处理 赫兹量化程序中的双精度数值时会出现。
1. 验证数值
为了验证计算结果和调试,你可以使用标准库 stdlib.mq4 的 DoubleToStrMorePrecision(double number, int precision) 函数,从而将双精度浮点数控制在指定的精确度。
这在搜索可能的错误时可以节省时间。
示例:
#include <stdlib.mqh> int start() { double a=2.0/3; Alert("Standard output:",a,", 8 digits precision:",DoubleToStr(a,8),", 15 digits precision:", DoubleToStrMorePrecision(a,15)); return(0); }
结果:
标准输出:0.6667,8 位精确度:0.66666667,15 位精确度:0.666666666666667
在某些情况下,为了显示双精度浮点数的数值(例如,在 Print, Alert, Comment中),最好使用 DoubleToStr 和 DoubleToStrMorePrecision 函数(stdlib.mq4)来显示更加精确的值代替标准的 4 位输出精确度。
例如:
#include <stdlib.mqh> int start() { double a=2.0/100000; Alert("Standard output=",a,", More precise output=",DoubleToStrMorePrecision(a,15)); return(0); }
返回: "Standard output=0, More precise output=0.000020000000000".

编辑切换为居中
2. 小数位精确度的准确度
由于是双精度浮点格式,其储存的准确度受到限制。
例如,如果我们假设精确度不受限制,从理论上讲,对于任何的双精度浮点数 A 和 B,以下的表达式始终成立:
(A/B)*(B)=A,
A-(A/B)*B=0,
(A/B)*(B/A)=1 等
小数位在计算机内储存的准确度取决于小数部分大小,并限制在 52 位。 为了说明该情况,赫兹量化来看看以下的例子:
在第一个循环(i)中,我们计算 23 的阶乘(从 2 到 23 的整数的乘积),结果为: 23!=25852016738884976640000. 结果储存在双精度类型的变量 a 中。
在下一个循环(j)中,我们将得到的结果 a 除以从 23 到 2 的所有整数。 最后我们期待的结果是 a=1。
#include <stdlib.mqh> int start() { int maxfact=23; double a=1; for (int i=2; i<=maxfact; i++) { a=a*i; } for (int j=maxfact; j>=2; j--) { a=a/j; } Alert(" a=",DoubleToStrMorePrecision(a,16)); return(0); }
实际上我们得到:
a=1.0000000000000002
可见,我们得到了 16 位的不准确度。
如果我们计算 35 的阶乘,则会得到 a=0.9999999999999998。
赫兹量化语言有一个 NormalizeDouble 函数,可以将双精度浮点数圆整为指定的精确度。
双精度类型的常量在内存中储存的方式跟双精度变量类似,因此有必要在定义它们时考虑限制在 15 位有效数字。
但不要将小数位精确度的准确度和双精度浮点数的计算精确度混淆。
双精度浮点数的可能值范围更宽:从 -1.7*e-308 到 1.7*e308。
赫兹量化来尝试估算双精度浮点数的最小指数。
int start() { double R=1; int minpwr=0; while (R>0) {R=R/10; minpwr--;} Alert(minpwr); return(0); }
程序会输出 -324,但我们必须考虑小数部分的小数位(+15),将会得到最小双精度浮点数的指数的近似值。
3. NormalizeDouble 函数
NormalizeDouble(double value, int digits)函数将浮点数值圆整到给定的精确度。 返回标准化双精度型数值。
例如:
int start() { double a=3.141592653589; Alert("a=",DoubleToStr(NormalizeDouble(a,5),8)); return(0); }
结果为:
a=3.14159000
注意:在交易操作中,是 不可能使用非标准价格的,因为其准确度至少超过了交易服务器所要求的一位。 挂单的止损、获利和价格值应以某精确度标准化,它的值储存在预定义的变量 Digits 中。
4. 检查两个双精度浮点数是否相等
建议使用 stdlib.mq4 库的 CompareDoubles(double number1,double number2) 函数 比较两个 双精度 浮点数, 如下所示:
//+------------------------------------------------------------------+ //| correct comparison of 2 doubles | //+------------------------------------------------------------------+ bool CompareDoubles(double number1,double number2) { if(NormalizeDouble(number1-number2,8)==0) return(true); else return(false); }
该函数比较双精度浮点数 1 和 2,精确度达到 8 位小数。
示例:
#include <stdlib.mqh> int start() {double a=0.123456781; double b=0.123456782; if (CompareDoubles(a,b)) {Alert("They are equal");} else {Alert("They are different");} }
输出为:
它们相等
因为它们只在第 9 位才不同。
如果必要,你可以类似的编写自己的比较函数(根据所需的准确度)。
5. 整数相除
需要记住,如果我们将两个整数相除,也会得到一个整数。
这就是为什么下面的代码:
int start() { Alert(70/100); return(0); }
会输出 0,因为 70 和 100 都是整数。
跟 C/C++ 语言一样,在 赫兹量化 中两个整数相除的结果将是整数,在本例中结果为 0。
但是,如果分子或分母是双精度(即具有小数部分),结果也将是双精度。 因此,Alert (70/100.0) 将输出正确值 0.7。
例如:
int start() { double a=1/3; double b=1.0/3; Alert("a=",a,", b=",b); return(0); }
将输出“a=0, b=0.3333”