根据ECMAScript 规范,Javascript 数值对应于双精度 64 位二进制格式 IEEE 754 值。
对于我目前正在研究的 WebIDL 验证器,我需要能够确定给定的数值是否可以转换为WebIDL 浮点类型,即它是否可以表示为有限单精度 32 位 IEEE 754 值.
我目前已经确定了以下方法:
validate: function(value) {
if (typeof value !== 'number' || !Number.isFinite(value)) {
return false;
}
if (value === 0) {
return true;
}
var view = new DataView(new ArrayBuffer(4));
view.setFloat32(0, value);
var converted = view.getFloat32(0);
var relativeError = Math.abs(value - converted) / value;
return relativeError < Number.EPSILON;
}
本质上,我正在做的是:
- 将 a 包裹
DataView
在 4 字节左右ArrayBuffer
。 - 将该
Number
值作为 32 位浮点数存储在缓冲区中。 - 将转换后的数字从缓冲区中取出。
- 计算原始值和转换值之间的相对误差。
- 检查相对误差是否小于浮点数的机器 epsilon
Number.EPSILON
,使用.
一些评论:
- 我将其作为 Chrome 扩展的一部分来实现,浏览器兼容性并不是真正的问题。
- 是的,我已经阅读了每个计算机科学家应该知道的关于浮点运算的知识。我的眼睛还没有止血。
上述逻辑是否适合我想要实现的目标?是不是矫枉过正?有没有更惯用、更优雅或更高效的方式来做到这一点?
更新
与此同时,我发现上述方法是不正确的。1.001
对于大范围的值(例如、等),它将失败3.14159
。另一方面,将 epsilon 值更改为 32 位浮点数 (2 −2316777217
) 的机器 epsilon 过于宽松,允许使用.
仍在寻找一个好的解决方案,但目前正在使用下面的函数,它只检查整数上限和下限(2 24和 -(2 24 ))。
validate: function(value) {
return typeof value === 'number' && Number.isFinite(value)
&& value >= -16777216 && value <= 16777216;
}