14

给定两个浮点数,我正在寻找一种有效的方法来检查它们是否具有相同的符号,假设如果两个值中的任何一个为零(+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

SameSign在 C++ 中一个天真的但正确的实现是:

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;
}

我正在寻找两件事:

  1. SameSign使用位技巧、FPU 技巧甚至 SSE 内在函数更快、更有效地实现.

  2. SameSign三个值的有效扩展

编辑:

我已经对三个变体SameSign(原始问题中描述的两个变体,加上斯蒂芬的一个变体)进行了一些性能测量。每个函数运行 200-400 次,在 101 个浮点数组中的所有连续值对上随机填充 -1.0、-0.0、+0.0 和 +1.0。每次测量重复 2000 次并保持最短时间(以消除所有缓存效应和系统引起的减速)。该代码使用 Visual C++ 2008 SP1 编译,并启用了最大优化和 SSE2 代码生成。测量是在 Core 2 Duo P8600 2.4 Ghz 上完成的。

以下是时序,不包括从数组中获取输入值、调用函数和检索结果的开销(总计 6-7 个时钟滴答):

  • 天真的变体:15 滴答声
  • 位魔法变体:13 个滴答声
  • Stephens 的变体:6 滴答声
4

3 回答 3

20

如果您不需要支持无穷大,您可以使用:

inline bool SameSign(float a, float b) {
    return a*b >= 0.0f;
}

这实际上在大多数现代硬件上都非常快,并且是完全可移植的。但是,它在 (zero, infinity) 情况下无法正常工作,因为零 * 无穷大是 NaN,并且无论符号如何,比较都会返回 false。当 a 和 b 都很小时,它也会在某些硬件上引起异常停顿。

于 2010-05-27T16:23:13.457 回答
5

也许是这样的:

inline bool same_sign(float a, float b) {
    return copysignf(a,b) == a;
}

有关其作用的更多信息,请参见 copysign 的手册页(您也可能需要检查 -0 != +0)

或者如果你有 C99 函数,可能是这个

inline bool same_sign(float a, float b) {
    return signbitf(a) == signbitf(b);
}

附带说明一下,在 gcc 上,至少 copysign 和 signbit 都是内置函数,因此它们应该很快,如果您想确保正在使用内置版本,您可以执行 __builtin_signbitf(a)

编辑:这也应该很容易扩展到 3 值情况(实际上这两者都应该......)

inline bool same_sign(float a, float b, float c) {
    return copysignf(a,b) == a && copysignf(a,c) == a;
}

// trust the compiler to do common sub-expression elimination
inline bool same_sign(float a, float b, float c) {
    return signbitf(a) == signbitf(b) && signbitf(a) == signbitf(c);
}

// the manpages do not say that signbit returns 1 for negative... however
// if it does this should be good, (no branches for one thing...)
inline bool same_sign(float a, float b, float c) {
    int s = signbitf(a) + signbitf(b) + signbitf(c);
    return !s || s==3;
}
于 2010-06-14T15:43:52.990 回答
0

关于符号位的一个小说明:宏返回一个 int 并且手册页声明“如果 x 的值设置了符号位,它返回一个非零值”。这意味着如果bool same_sign()符号位针对两个不同的负值返回两个不同的非零整数,则不保证 Spudd86 可以正常工作。

首先转换为 bool 可确保正确的返回值:

inline bool same_sign(float a, float b) {
    return (bool)signbitf(a) == (bool)signbitf(b);
}
于 2015-06-10T07:11:38.637 回答