编译时检查右移是否是对有符号类型的算术运算

时间:2011-02-25 22:16:15

标签: c++ c portability bit-manipulation compile-time

我想知道在编译时操作签名类型(例如-2 >> 1是否-1)时,检查右移是否算术的最便携方法是什么。

我的想法是在编译时以某种方式检查它并能够检测到这一点,因此我可以编译该函数的不同版本(取决于运算符>>是否真的是算术移位)。

通过阅读主题 Verifying that C / C++ signed right shift is arithmetic for a particular compiler?我想到初始化一个标志

static const bool is_arithmetic_rs = (((signed int)-1)>>1) == ((signed int)-1));

并在运行时测试它,如下所示:

if (is_arithmetic_rs) {
  // some fast algorithm using arithmetic right shifts (using >> operator)
} else {
  // the same algorithm without arithmetic right shifts (much slower)
}

但是,我希望每次都尽可能避免这种分支。为简单起见,我们假设我想实现一个可移动的算术右移;如果每次调用函数时都要检查这个,这会对性能造成巨大影响,所以我想在编译时这样做,如果可能的话。

如果没有可移植的方法来进行此检查,是否有办法通过检查尽力而为的方式来执行此操作,例如使用ifdef检查特定的编译器/平台?

7 个答案:

答案 0 :(得分:8)

执行此类检查的最佳方法是GNU autotools做:

  • 在目标平台上编译一个小程序并测试会发生什么

  • 在标题文件中设置适当的#define

  • 在源文件中包含该头文件

  • (可选)使用适当定义的宏,这样就不会为每个小事件使用#ifdef指令来混淆代码。

  • 编译主项目

通过这种方式,您可以避免必须创建具有受支持功能的表以及每个硬件平台和操作系统的各种怪癖 - 更不用说它们的组合了。但是,如果您不在目标上构建代码,必须使用预先提供的目标表/功能列表替换第一步。

您应该查看广泛使用的构建系统,例如GNU autotools或CMake,以便重用现有的宏和特定于平台的信息,避免创建自己的构建系统,从而重新发明轮子。< / p> 顺便说一句,现在任何体面的编译器都应该使用常量表达式优化出简单的测试,因此在必要时使用运行时测试 - 可能通过宏 - 不应该对性能造成太大影响。您应该测试并分析您的代码以找出答案。

答案 1 :(得分:7)

使用预处理时间测试可以避免分支

#if ((-1)>>1) == (-1))
...
#else
...
#endif

答案 2 :(得分:4)

更多的是评论而不是回答(但显然我并不信誉)

这里的几个答案使用预处理器检查,如

#if ((-1)>>1) == (-1))

就个人而言,我不相信预处理器会告诉我编译器生成什么样的代码。

答案 3 :(得分:3)

您是否确实已经验证过您的编译器在可用时不会将除法优化为算术移位?

否则我认为你可以使用模板。

template <bool B>
void do_work();

template <>
void do_work<true>()
{
    // Do stuff with H/W.
}

template <>
void do_work<false>()
{
    // Do slow stuff with S/W.
}

do_work<(-2 >> 1) == -1>();

然后使用内联函数更漂亮:

inline real_do_work()
{
    do_work<(-2 >> 1) == -1>();
}

答案 4 :(得分:1)

试试这个:

#define SAR(x,y) ((x)>=0) ? ((x)>>(y)) : (~(~(x)>>(y)))

一个优秀的编译器会将此优化为((x)>>(y)),假设CPU是理智的。

欢迎反馈哪些编译器是好的。

答案 5 :(得分:1)

灵感来自Giuseppe'sR..'s的答案:

#if -2 >> 1 == -1
#define rshift_ar(X, Y) ((X) >> (Y))
#elif ~(~(-2) >> 1) == -1
#define rshift_ar(X, Y) ((X) >= 0 ? (X) >> (Y) : ~(~(X) >> (Y)))
#else
#error "unsupported shifting semantics"
#endif

答案 6 :(得分:0)

所有这些预处理器魔法对于任何体面的编译器都是无用的。您是否验证上面给出的代码确实在其输出中生成了分支?我非常怀疑它,因为static const bool可以将a评估为编译时常量,因此编译器将消除false - if (is_arithmetic_rs)的一部分,其中任何高于{ {1}}。另请参阅我的回答here

此外,我怀疑预处理器的输出保证与编译器的输出相同,特别是在具有不同移位的平台之间进行交叉编译时。