总而言之,我有一段代码,已有十多年的历史,我们和外部客户都在使用它。我们有一个“移位”号码,通过它我们可以移动一个移动窗口。它被设计为一个整数,因为,我们正在查看数据中的不同位置,因此没有分数移动的概念。现在,他们希望能够有一个非整数班次号码。不是插值,而是简单地让程序进行整数移位,但是当我们通过该边界时,它会稍微移动。
一个例子可能更有意义。假设我们有10个班次。这些职位将如下: 0,10,20,30,40等
现在,我们希望能够设置10.4的班次。因此,我们希望转变如下: 0,10,20, 31 ,41等
10.5的转变将改为0,10,21,31,42等等。
基本上,我们总结了小数位,当它超过小数点时,我们只需再移动一位。当然,正如浮点运算经常发生的那样,我们遇到了潜在的准确性问题,但我们也希望保持加速。一种天真的方法是在开始时将该小数位分开并保持对其求和,检查其值并在其达到1.0时递减。这样做的好处是可以遵循我倾向于考虑操作的方式,但它涉及每次迭代的条件检查,并且通常存在累积错误的可能性。
我还可以看到预先计算在我们必须检查它是否超过1.0之前我们可以添加该小数位的次数(因此,如果我们的小数位为0.5,我们知道我们只需要每隔一次检查一次或者如果它是0.3,我们知道我们只需每四个左右检查一次。)
处理重复求和的常用方法当然是用乘法来代替它,但是在这里,我们并不关心实际的总和,因为我们预测哪些帧需要“再换一次”最后让事情匹配。
我们的典型任务涉及这个类在相对较小的因子组合上运行,例如迭代96.46875的移位少于3000次。但是,不能保证这种限制仍然有效,所以我被告知可能会有人将窗口移动一千万次,我们仍然想知道移动的距离。
有什么建议吗?
答案 0 :(得分:1)
考虑将shift
设置为最接近所需值的双精度值并稍微增加(仅一次):
shift = nexttoward(shift, INFINITY); // Ensure shift is above the threshold.
然后,要计算当前位置,请使用:
result = floor(step * shift);
当step
的产品和shift
中的错误接近一个产品时,这可能会产生太大的值。 (乘法本身也可能存在轻微的舍入误差。)但是,对于许多步骤不会发生这种情况,如下所示。
shift
中的错误最多为1.5 ULP(初始从小数转换为.5 ULP,nexttoward
为1)。如果shift
小于1024,则ULP小于2 10-52 。如果step
最多为10,000,000,则误差小于10,000,000•1.5•2 10-52 ,约为3.41•10 -6 。因此,距离产生不正确结果所需的幅度还有很长的路要走。
如果您通过每次添加shift
累计计算结果,而不是通过新的乘法,那么可能会有其他错误。这些可能仍然太小而不会导致错误,但应对它们进行评估。
如果达到上述限制,有办法进一步减轻错误。
答案 1 :(得分:0)
为什么不使用地板功能。如果没有看到你有什么代码,这里有一个猜测是什么工作
for (int i=0; i < 3000; i++)
{
cout << static_cast<int>(floor(i*shift)) << '\n';
}
有人可能反对这种方法的效率,但如果你说的不到3000次迭代,你就可以了。