给定两个浮点数,我正在寻找一种高效的方法来检查它们是否具有相同的符号,假设如果其中任何一个值为零(+0.0或-0.0),则应视为具有相同的符号。
例如,
- SameSign(1.0, 2.0) 应返回 true
- SameSign(-1.0, -2.0) 应返回 true
- SameSign(-1.0, 2.0) 应返回 false
- SameSign(0.0, 1.0) 应返回 true
- SameSign(0.0, -1.0) 应返回 true
- SameSign(-0.0, 1.0) 应返回 true
- SameSign(-0.0, -1.0) 应返回 true
C++中一个朴素但正确的实现SameSign
是:
bool SameSign(float a, float b)
{
if (fabs(a) == 0.0f || fabs(b) == 0.0f)
return true;
return (a >= 0.0f) == (b >= 0.0f);
}
假设采用IEEE浮点数模式,这里有一个 SameSign
的变体,它能编译成无分支代码(至少在Visual C++ 2008中是如此):
bool SameSign(float a, float b)
{
int ia = binary_cast<int>(a);
int ib = binary_cast<int>(b);
int az = (ia & 0x7FFFFFFF) == 0;
int bz = (ib & 0x7FFFFFFF) == 0;
int ab = (ia ^ ib) >= 0;
return (az | bz | ab) != 0;
}
假设已经定义了binary_cast
:
template <typename Target, typename Source>
inline Target binary_cast(Source s)
{
union
{
Source m_source;
Target m_target;
} u;
u.m_source = s;
return u.m_target;
}
我正在寻找两个东西:
使用位运算、FPU 技巧甚至 SSE 指令集的更快、更高效的 SameSign 实现。
将 SameSign 扩展到三个值的高效方法。
编辑:
我对 SameSign 的三种变体进行了一些性能测试(原问题描述的两个变体以及 Stephen 的变体)。每个函数在一个由 101 个随机填充为 -1.0、-0.0、+0.0 和 +1.0 的浮点数数组中的所有相邻值上运行 200-400 次。每个测量重复 2000 次,并保留最小时间(以消除所有缓存效应和系统引起的减速)。代码使用 Visual C++ 2008 SP1 编译,启用最大优化和 SSE2 代码生成。测试在 Core 2 Duo P8600 2.4 Ghz 上完成。
以下是计时结果,不包括从数组获取输入值、调用函数和检索结果的开销(这些开销为 6-7 个时钟周期):
- Naive 变体:15 个时钟周期
- Bit magic 变体:13 个时钟周期
- Stephen 的变体:6 个时钟周期