确定双精度值在功能上是否为零的最快方法是什么

What is the fastest way to determine if a double is functionally zero

提问人:rjzii 提问时间:11/17/2023 最后编辑:rjzii 更新时间:11/17/2023 访问量:194

问:

我正在努力重构一些处理大量浮点计算的代码以提高性能(根据 valgrind 的说法,我们大约 90% 的时间都花在这个函数上,所以优化很好);但是,如果我们遇到实际上为零的情况,那么我们可以分配零并继续前进。以前我们执行以下操作(假设内联函数):x

if (std::fabs(a - b) < std::numeric_limits<double>::epsilon()) {
   // do things
}

在大多数情况下。而现在,当我知道它是零时,我正在改变以下模式:b = 0.0b

if (std::abs(a) < std::numeric_limits<double>::epsilon()) {
  // do things
}

这似乎为我们节省了几个周期。但是,是否可以采取更多措施来加快速度,或者这或多或少是门槛?

C++ 性能

评论

0赞 Joop Eggen 11/17/2023
b 接近于零?- b ḿissing?
0赞 user207421 11/17/2023
std::fpclassify()不处理 epsilon,只处理 +/-0。
0赞 ShadowRanger 11/17/2023
@user207421:这可能不完全是一回事,但它是一种排除次正常数字的方法(非常接近零的数字,如果不是为接近零的数字保留的特殊类别,它们就无法用正常精度规则表示)。 on 和 treat 和 is 等价(两者都“有效”为零)。doubleswitchstd::fpclassifyFP_ZEROFP_SUBNORMAL
1赞 Marc Glisse 11/17/2023
如果你认为低于 epsilon 的任何值为 0,那么你并不是在做浮点,更接近定点。有没有办法只使用整数?
1赞 Pete Becker 11/17/2023
回复:“计算概率......必须是浮点型“——没有必要。概率范围从 0 到 1。如果您需要四位数的精度,请将所有内容乘以 10000 并使用整数。这显然过于简单化了,但它说明了基本思想。

答:

6赞 ShadowRanger 11/17/2023 #1

我认为这与它将要得到的效率差不多。启用优化后,在 x86-64 系统上,生成的程序集为:if

  1. A(将 的值从全局常量存储器加载到寄存器;函数变成一个要加载的常量)movsdepsilon
  2. 一个(为了掩盖被检查中的负号位,这就是内联时所要做的)andpddoublestd::abs
  3. A(比较两个寄存器)comisd
  4. A(实际条件分支)ja

如果为更现代的芯片编译,前三条指令将更改为前缀形式(我猜新版本在可用时效率更高),但除此之外不会更改。这几乎是便宜的。我想从理论上讲,如果你经常这样做,矢量化可能会让你用更少的指令对多个值做同样的工作,但是v

  1. 编译器可能会自行解决这个问题,并且
  2. 如果没有,我会等待分析显示这是一个瓶颈,然后再进一步担心

据我所知,任何基于(您想要并被视为等效的地方)的解决方案,例如:std::fpclassifyFP_ZEROFP_SUBNORMAL

switch (std::fpclassify(a)) {
case FP_SUBNORMAL:
case FP_ZERO:
    // do things
    break;
}

涉及相同的指令,加上三个(或)指令,以及三个条件跳转;几乎可以肯定,两个额外的比较和条件分支比加载单个常量更糟糕。andpdcomisducomisd

1赞 bolov 11/17/2023 #2

您可以考虑的另一个优化是丢弃更多情况:

constexpr float near_zero_limit = 0.01f;

if (std::fabs(a - b) < near_zero_limit )

这并不是说测试更快,而是在更多情况下你不做剩下的工作。我不知道你的算法,也不知道这对你的计算是否有意义,但你可以搜索最大的算法,你可以接受将最终结果视为有效的零或从零开始察觉不到,如果这会带来更高的性能。near_zero_limit