MQL5语言帮助文件人话版(六)数据类型之浮点数据类型

| 发表于 前天 12:12 | 显示全部楼层 |复制链接
第六章 浮点数据类型

一、编程新手感悟:关于数据精度

在整数数据类型中,我们讨论最多的是数的大小。但在浮点数类型中,我们要重点考虑的是数字的精度

通常我们需要操作小数的时候,默认使用双精度浮点型 double 类型。这个类型可以存储的数字上下限达到 10 的 308 次方。如果从数字大小的角度考虑,整个可观测宇宙的原子总数量,都比这个数值小很多数量级。

那我们为什么程序里要频繁使用一个最大范围大到不可思议的数据类型?其实直接因素是因为浮点数的绝对上下限是实际中不可能超越的数字,这个超大取值范围,并不是用来提升精度,而是为了容纳运算过程中产生的中间临时数值,避免计算中途直接溢出变成无穷大 inf。

真正决定精度的,是浮点数的尾数部分,308次方所带来的那么多一堆0,绝大多数是没有精度上的意义的。

举个例子,全球黄金市场日均成交量为 486957832517.65 美元(示例虚构),大概 4869.58 亿。前文说过,如果只想记录整数部分,用 long 类型就可以。如果想要精确记录到美分,可以把全部金额换算成以 “美分” 为单位的整数存入 long,使用的时候再除以 100 还原成美元。这就是定点的思路。

但很多业务场景我们并不需要这么高的绝对整数精度。整数数据类型在其值范围内,都是精确数,写多少就是多少,比如 long 在它的取值范围内,计数精度绝对碾压浮点数;但数字一旦超过整数类型的上下限,就会出现溢出错误。

对于日均几千几万亿的市场,很多时候我们并不关心千万、百万级别以下的微小差异。这时就可以灵活切换数字的量级:比如 0.49 万亿,也可以写成 4869.58 亿。

所谓 “浮点”,就是小数点可以自动浮动。并不是我们手动指定小数点写在哪个位置,而是硬件会根据数值大小,自动调整小数点的位置。同一个格式,既能表达极大的数,也能表达极小的数。

单精度浮点数 float 拥有 6‑7 位有效十进制数字;双精度 double 拥有 15‑17 位有效十进制数字。这里说的有效位数,指从左边第一个非 0 数字开始数的总位数,就是前面说的所谓尾数,并不是小数点后面有多少位。

例如1.23456789和123456.789,两者都是 9 位有效数字,精度要求是一致的。如果交给 float 存储,超出 7 位的部分就会失真;使用 double 才可以完整保留这些有效位。

虽然 float、double 能够表达 10 的 38 次方、10 的 308 次方这种天文级别的大小,但取值范围巨大不等于精度无限。当数字的有效总位数超过它的承载能力,多余的低位会按照二进制就近舍入规则丢弃,得到一个距离原值最近的可存储数值,原始信息就此丢失。

二、浮点数

计算机把数字分成两大类:

(1)整型 int/long/char:只能存整数,没有小数点,比如:5、-100。

(2)浮点型 float /double:可以保存带小数的数值,比如:2.5、-0.00136、123.889

浮点型(floating-point number)用来存储带有小数部分的数值。

MQL5 提供两种浮点类型:float、double。

浮点型数据由整数部分、小数点“.”和小数部分组成,其中整数部分和小数部分为一系列十进制数字。

内存中浮点数的存储格式遵循 IEEE 754 国际标准;这套标准和硬件平台、操作系统、编程语言无关,是通用规范。

IEEE 754 规定了两种常用精度格式:

(1)float(单精度浮点数):占用 4 字节(32 bit);取值范围1.175494351e-38到3.402823466e+38。精度在6~7位,较低,占用内存小。

(2)double(双精度浮点数):占用 8 字节(64 bit);取值范围2.2250738585072014e-308~1.7976931348623158e+308。精度在15~17位,更高。

MQL5 开发(EA、指标)绝大多数场景优先使用 double。比如行情价格、K 线高低点、指标计算返回值全部默认是 double。

注意:浮点数不是精确小数!IEEE754 用二进制表达小数,很多十进制小数无法精准保存,你可以理解为,有些小数转化为二进制时,会形成一种二进制的无限循环。

特殊说明:0.1 在 double 里是无限循环二进制,只能近似存储。比如要设计在1.1元时开单,不能写成“if(d == 1.1)”,这样写,系统将永远无法捕捉到d为1.1的情况。正确思路应采用判断两个浮点数差值足够小,认为近似相等。通常做法是把容差 EPS 设置成略大于该品种最小报价步进,会更加稳定。

品种报价精确到小数点后 5 位,EPS 设为 1e-6 就足够。要找一个向上突破1.1的入场价,就写成if(price > 1.1 + EPS),这样就可以了。

三、科学计数法浮点数:

在代码中,1.12123515e-25等同于1.12123515 ×10^25。

e 代表 10 的幂次。e-25,代表小数点向左移动 25 位。

如果要硬写,就是0.000000000000000000000000112123515,小数点后有24个零。

这两种写法在内存中是同一个浮点数。但科学计数法书写更简洁。

在输出层面,因为数据类型精度位数的问题,比如double类型,也只有15到17位精度,显示不了24位数。上面的数字显示出来,全都是 0,但这个变量不等于 0,这是展示方式造成的视觉假象。输出的时候用科学计数法,可以把后面的有效数字强行提到前面来,规避这种输出一大串0的现象。

如果要观测极小数值真实大小,使用负数参数开启科学计数输出。

四、非法浮点数:

浮点运算中,部分运算会产生非法浮点特殊值(NaN、正负无穷),这类不属于正常有效小数。不要拿这些无效浮点数参与常规计算、大小比较;对无效浮点数执行运算,最终结果行为是未定义、不可预测的。

浮点特殊值一共三类:正无穷 (+Inf)、负无穷 (-Inf)、NaN (非数字),虽然我们写代码基本上不会用到这些符号,这些数据也没办法参与计算,但如果我们设计的计算规则不合理,很大可能在输出结果中出现这些符号,所以还是要认识它。

想要检测一个 double 是不是合法有效数值,可以使用 MQL5 内置函数 MathIsValidNumber()。

按照 IEEE 754 规范,这些特殊值在内存里有固定的二进制位模式(十六进制编码)。

举例:双精度的正无穷,对应的 64 位十六进制码是 0x7FF0000000000000。
最近访问 头像模式
举报

评论 使用道具

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册