有效检查两个浮点值是否具有不同的符号

Efficient check that two floating point values have distinct signs

我需要找出两个有限浮点值 AB 是否有不同的符号或其中之一为零。

在很多代码示例中我看到测试如下:

if ( (A <= 0 && B >= 0) || (A >= 0 && B <= 0) )

它工作正常但对我来说看起来效率低下,因为这里验证了许多条件并且每个条件分支对于现代 CPU 来说都是一个缓慢的操作。

另一种选择是使用浮点值的乘法:

if ( A * B <= 0 )

即使在溢出的情况下它也应该工作,因为无穷大值保留适当的符号。

执行检查的最有效方法是什么(这两种方法之一或其他方法)?

It works fine but looks inefficient to me since many conditions are verified here and each condition branch is a slow operation for modern CPUs.

优化编译器不会产生不必要的分支。除非您处于非常紧密的热循环中,否则不要担心过早的优化。在我试过的编译器中,你的第一个片段被编译成 x86-64 和 ARM64 中的 2 个分支。它也可以编译成无分支版本。当然,它可能仍然比简单的 A * B <= 0 慢,但如果没有适当的基准测试

,就无法确定这一点

如果你不关心 NaN 那么你可以简单地做一些按位运算:

auto a = std::bit_cast<uint64_t>(A);
auto b = std::bit_cast<uint64_t>(B);
const auto sign_mask = 1ULL << 63;
return ((a ^ b) & sign_mask) || (((a | b) & ~sign_mask) == 0);

如果 A 和 B 的符号不同,那么它将被 (a ^ b) & sign_mask 匹配。如果它们具有相同的符号,则它们都必须为零,这将被后一种情况捕获。但这适用于整数,因此当将值从 float 移动到 int 域时可能会导致

如果 std::bit_cast 不可用,则只需替换为 memcpy(&a, &A, sizeof A)

Demo on Godbolt

再次进行基准测试以确定最适合您的目标。没有一种解决方案在所有可用的微体系结构上都是最快的。如果你真的 运行 在一个循环中很多次那么你应该使用 SIMD instead to check for multiple values at the same time. You should also use profile-guided optimization 以便编译器知道分支的位置和时间

Efficient check that two floating point values have distinct signs

AB 属于集合 -0.0, +0.0.

时,

if ( A * B <= 0 ) 无法区分符号

考虑signbit()

if (signbit(A) == signbit(B)) {
  ; // same sign
} else  {
  ; // distinct signs
}

相信编译器可以生成高效的代码 - 或者使用更好的编译器。


OP 然后形成了一个不同的目标:“ether 两个有限浮点值 A 和 B 具有不同的符号或其中一个为零。”

当然 if (signbit(A) != signbit(B) || A == 0.0 || B == 0.0) 满足这个更新的 功能 而无需四舍五入到 0.0 问题 of if ( A * B <= 0 ),但可能不满足模糊最有效方式的要求

形成高效代码并避免premature optimization really the root of all evil的最佳方法是查看更大的上下文。