【问题标题】:Purpose of bitwise OR of an integer with its negative整数与负数按位或的目的
【发布时间】:2014-11-12 08:34:09
【问题描述】:

我很好奇 NaN 在 IEEE 单精度和双精度浮点中的实现和表示,我发现 this implementation 是一个“is NaN”函数。即:

int isnan(double x)
{
    int32_t hx,lx;

    // Move lower 32 bits of double to lx, higher 32 to hx.
    EXTRACT_WORDS(hx,lx,x);

    // Remove sign bit, since -NaN and NaN are both NaN.
    hx &= 0x7fffffff;

    // Equivalent to hx |= (lx != 0).
    hx |= (u_int32_t)(lx|(-lx))>>31;

    // Difference is negative iff (hx & 0x7ff00000) == 0x7ff00000 and (hx & 0x000fffff) != 0.
    hx = 0x7ff00000 - hx;
    return (int)((u_int32_t)(hx))>>31;
}

我不明白(lx|(-lx)) >> 31 的用途,在我脑中推理失败后,我对所有整数进行了测试,发现lx = 0 的结果为0,否则为1。

我能想到的唯一原因是,可能无法使用 (lx != 0) 代替,因为某些 C 标准没有定义分配给真操作的整数值(例如,不保证为真时为 1)或者也许!= 比负或位移位要慢。否则,我难住了。

作为参考,我用来尝试所有整数的代码,以防出错。

#include <stdio.h>
#include <stdint.h>
int main(void) {
        int32_t i = 0;
        do {
                if (((uint32_t)(i | (-i)) >> 31) == 0)
                        printf("%d\n", i); // prints only 0
        } while (i++ != 0xFFFFFFFF); // overflows to -max_int and then climb to -1
        return 0;
}

【问题讨论】:

  • 分别打印出lx-lx 的位模式,看看你会发现什么。
  • 移除条件跳转。
  • Joachim,现在我看到了结果,我明白为什么它只有 1 比 0,因为如果它不为零,那么 lx 或 @ 的最高有效位(符号位) 987654332@ 将是 1(因此当您向右位移 31 位时,它将是 1)。我的问题是为什么不使用(lx != 0) 而不是比较。
  • Michael,据我了解,如果指数位全为 1 且有效位/尾数位全为 0,则浮点数为 NaN。我试图解开那个表达式的奥秘时遇到了障碍,所以我没有太深入地研究函数的其余部分,但我的直觉说它应该起作用。由于双精度的低 32 位都是有效数的一部分,我相信如果低位中有任何非零位,我认为这个想法是在高位 int32 的末尾放置一个 1 (因此,与0) 然后从上半部分开始检查有效位中的任何 1。
  • @Jengerer:谢谢 - 我不确定在我之前尝试解决代码正在做什么时我做错了什么,但你已经击中了它(除了 NaN尾数非零;如果尾数全为零,则表示无穷大)。这再次证明我应该始终避免处理浮点问题。

标签: c integer bitwise-or


【解决方案1】:

表达式(u_int32_t)(lx|(-lx))&gt;&gt;31 等价于lx==0? 0:1

但是,使用lx==0? 0:1,您将分支操作强加到目标代码中。

与几个按位操作相比,这可能会降低性能。

这实际上取决于底层硬件架构以及手头指定的编译器。

但它肯定会导致性能不一致,具体取决于分支预测启发式。

(u_int32_t)(lx|(-lx))&gt;&gt;31 的运行时间保证每次执行都相同。

【讨论】:

  • abs(lx)lx 的绝对值,与lx|-lx 没有任何相似之处
  • @Jengerer:不管上述情况如何,您可能会很高兴知道您的问题实际上已经回答了我不久前在这里发表的一个问题:stackoverflow.com/q/22500119/1382251。跨度>
  • @Jengerer:请注意您从文档中引用的“如果”。这是非常简单的分支,这里没有什么太复杂的地方。 sne 确实根据ZF 标志的值执行立即操作,但是为了使该标志根据输入参数的值获得 0 或 1 值(在我们的示例中为lx) ,处理器必须做出“分支决定”(例如,如果lx == 0,则ZF = 1,否则ZF = 0)。
  • @barakmanos:很好,我不认为实际的比较可能需要进行一些分支来设置ZF 标志。
  • @Jengerer:与“OR 技巧”相比,分支不一定会降低性能(尽管我相信它会这样做,至少在大多数架构上)。但由于输入参数的值是未知的,它将产生不一致的性能,具体取决于该值,并受到底层硬件架构的分支预测启发式算法的影响。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-27
  • 1970-01-01
  • 2016-03-23
  • 1970-01-01
相关资源
最近更新 更多