我想要一个创建增加浮点数组的函数。当增量无法用二进制精确表示时,我遇到了问题。增量为0.1的示例,其二进制表示无限重复。
var incrementalArray = function(start, end, step) {
var arr = [];
for (var i = start; i <= end; i += step) {
// 0.1 cannot be represented precisely in binary
// not rounding i will cause weird numbers like 0.99999999
if (step === 0.1) {
arr.push(i.toFixed(1));
} else {
arr.push(i);
}
}
return arr;
};
此实现存在两个问题。 A)仅涵盖增量为0.1的情况。是否有许多其他具有重复二进制表示的魔术数字? B)当增量为0.1时,返回的数组不包含结束值。但是,它确实包含了增量时的结束值,使得此函数不可预测。
incrementalArray(0.0, 3.0, 0.1);
// [0.0, 0.1, 0.2, .... 2.8, 2.9]
incrementalArray(2,10,2);
// [2, 4, 6, 8, 10]
这个功能如何适用于所有特殊增量并且可以预测?
答案 0 :(得分:2)
如果您将自己局限于有理数,则可以用整数表示步骤,并在将每个条目转换为浮点数时将整数索引除以单个分母(如果必须以该格式表示)。
答案 1 :(得分:1)
我认为这可行:
var incrementalArray = function(start, end, step) {
var arr = [];
// convert count to an integer to avoid rounding errors
var count = +((end - start) / step).toFixed();
for (var j = 0; j <= count; j++) {
var i = start + j * step;
arr.push(i);
}
return arr;
};
输出:0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000001, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000001, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000002, 2.4000000000000003, 2.5, 2.6, 2.7, 2.8000000000000002, 2.9000000000000003, 3
如果要将截断的输出截断为与输入相同的小数位数,可以使用:
var incrementalArray = function(start, end, step) {
var prec = ("" + step).length - ("" + step).indexOf(".") - 1;
var arr = [];
// convert count to an integer to avoid rounding errors
var count = +((end - start) / step).toFixed();
for (var j = 0; j <= count; j++) {
var i = start + +(j * step).toFixed(prec);
arr.push(i);
}
return arr;
};
输出:0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0
答案 2 :(得分:1)
仅供参考,问题实际上比您可能意识到的要复杂得多,因为在IEEE754二进制浮点表示中,数字线的间距不均匀 - 随着幅度的增加,最小可检测差异(LDD)也会增加(即可表示)数字越来越远。)
意味着什么非常简单:如果 step 的绝对值小于LDD,您可以使用参数调用此例程,这些参数将导致无限循环在由开始和结束界定的区间内。
我知道确定实数行中任意点的最不可检测差异的最简单方法是这个例子(关于匹配数据类型大小的常见警告) - 对值没有意义,它只是简单的随机数:
real_32 A = 999.111f;
uint_32 B = (*(uint_32*) &A) ^ 1; //create a 1-bit difference
real_32 LDD = abs(A - *(real_32*) &B);
FWIW您可以轻松使用此机制的变体来确定两个实数是否在彼此的LDD范围内,并认为“相等”,因为直接比较实数是有问题的。
答案 3 :(得分:0)
如何将它们扩展为整数然后退回? (demo)
function incrementalArray(start, end, step) {
step += "";
var scale = Math.pow(10, step.length - step.indexOf(".") - 1);
start *= scale;
end *= scale;
step *= scale;
var values = new Array();
for (var x = start; x < end; x += step) {
values.push(x / scale);
}
return values;
}