这个数学四舍五入函数是如何工作的?

5
有人能解释一下这个函数是做什么的吗?
static inline void round_to_zero(volatile float *f)
{
  *f += 1e-18;
  *f -= 1e-18;
}

我指的是除了加1e-18并再次减去之外,我理解这一点。但我不明白它对传递给它的浮点数会产生什么影响。我试图理解它的原因是在某些使用此函数的代码中使用了双精度浮点数(我已将其从浮点数转换)。这是音频代码,上述函数来自于此库:

https://github.com/swh/lv2/blob/master/include/ladspa-util.h

我想知道它是否可以像现在这样在双精度浮点数上运行,或者需要修改以适应双精度浮点数的额外精度。我怀疑这会削减数据的最后几位,如果它们存在于浮点数中,则从浮点数中消除它们,尽管我不太明白如何做到这一点。但是我想象如果是这样,我将需要更改指数以适应双精度浮点数。

TIA,Pete


看起来这是一个严重优化的代码,它依赖于IEEE754处理带有代码处理的预期数据类型的非规范数(http://en.wikipedia.org/wiki/Denormal_number)。除此之外,我无法理解它。 - caskey
@caskey float的非规格化数比那还要小。 - ouah
2个回答

1
以下代码演示了该函数的作用。
int main( void )
{
    float a;

    a = -1.0;
    a /= 1e100;
    printf( "%f\n", a );

    round_to_zero( &a );
    printf( "%f\n", a );
}

你需要知道的是,IEEE-754浮点数在0时有两个可能的值。有一个正零和一个负零round_to_zero函数将负零转换为正零。
对于双精度数1.0,值1e-18约等于1 lsb。因此,我认为使用该函数与double无需进行任何修改(当然,除了更改参数类型)。

谢谢 - 你能解释一下它是如何做到的吗?为什么加上 1e-18 并减去它会产生这样的效果,我想知道它对可能传递的所有数字范围产生什么影响。如果值 1e-18 是双精度浮点数的 1 lsb,则对于浮点数来说必须小得多,而代码是设计用于处理浮点数的,所以我想知道该函数是否对浮点数和双精度浮点数有不同的影响?由于我真的不理解(尽管我理解需要删除符号),我怀疑微妙差异可能会使使用双精度浮点数的代码表现出不同的性能(未被检测到)。 - Pete
@Pete -- 这个想法是,如果你从负0开始,那么加上1e-18会给你一个小的正数,而减去1e-18会给你一个正0。如果你从任何其他数字开始,加上和减去1e-18对数字没有净影响。这似乎是理论。 - user3386109

1

我觉得应该回来补充以下细节。

虽然将负零转换为正数的答案是正确的并对我有用,但这还不够。

添加1e-18然后从浮点数中减去它确实会清除浮点数中非常小的数字。这在音频应用程序中使用,因为滤波器可以通过不断地分割浮点数而使小浮点数循环通过函数,从而导致数字变得越来越小。一旦数字变得非规格化(如Caskey所提到的),许多CPU(包括x86)中该数字的处理速度会变慢高达100倍。

通过添加比该数据类型的非规格化大小数字大得多的数字,您可以清除类型中存储的微小值。从相同的较大值中减去会导致类型保持零,如果处理,则不会影响处理速度。您清除微小值的原因是类型中的有效位精度不足以容纳非常微小的值和刚刚添加的较大值。

例如:

从值为1.0f的音频样本开始。

将其通过一个除以10的函数处理40次,结果为1e-40。

v = 0.0100000 e-38(浮点类型大约有8位小数精度,指数最高可达38,因此在内存中看起来就像我在这里写的那样)。

现在,这是浮点类型的非规格化值,会导致CPU处理它非常缓慢。如何消除减速?将其变为零。所以:

添加1e-18;结果: 1.00000000 e-18(注意,原始的1e-40太小了,无法在已经持有更大的1e-18值的8位有效数字中表示)。

然后减去1e-18值: 0.00000000 e-0

因此,我们产生了零,清除了原始的非规格化值,我们的CPU感谢我们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接