5

我想知道在编译时对有符号类型(例如是否-2 >> 1为)进行操作时检查右移是否是算术的最便携的方法是什么。-1

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

通过阅读主题 验证 C/C++ 签名右移是否是特定编译器的算术?我想到了初始化一个标志的想法

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

但是,如果可能的话,我想每次都避免这种分支。为简单起见,假设我想实现一个可移植的算术右移;如果我必须在每次调用函数时都检查它,这将对性能产生巨大影响,所以如果可能的话,我想在编译时进行检查。

如果不存在进行此检查的可移植方法,是否有办法通过尽最大努力检查来做到这一点,例如使用 ifdefs 检查特定编译器/平台?

4

7 回答 7

8

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

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

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

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

  • 或者,使用适当定义的宏,这样您就不会#ifdef因为每件小事都使用指令而使代码混乱。

  • 编译你的主项目

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

您可能应该查看广泛使用的构建系统,例如 GNU autotools 或CMake,以便重用现有的宏和特定于平台的信息,避免必须创建自己的并因此重新发明轮子。

顺便说一句,如今任何体面的编译器都应该优化带有常量表达式的简单测试,因此在必要时使用运行时测试 - 也许通过宏 - 不会对性能造成太大影响。您应该测试和分析您的代码以找出答案。

于 2011-02-25T22:22:58.200 回答
7

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

#if ((-1)>>1) == (-1))
...
#else
...
#endif
于 2011-02-25T22:31:09.877 回答
5

真的更多的是评论而不是答案(但显然我没有信誉)

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

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

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

于 2012-03-09T13:16:07.997 回答
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>();
}
于 2011-02-25T22:39:09.253 回答
1

试试这个:

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

一个好的编译器会优化它以((x)>>(y))假设 CPU 是正常的。

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

于 2011-02-25T23:14:32.087 回答
1

启发GiuseppeR.. 的回答:

#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
于 2011-02-26T11:40:03.877 回答
0

所有这些预处理器的魔法对于任何体面的编译器都是无用的。您是否验证了上面给出的代码确实在其输出中生成了一个分支?我非常怀疑它,因为static const boolcan an 将被评估为编译时间常数,因此编译器将消除false您的 - 部分,以及上面的任何内容。另请参阅我的答案if (is_arithmetic_rs)-O1

此外,我怀疑预处理器的输出是否保证与编译器的输出相同,尤其是在具有不同班次的平台之间进行交叉编译时。

于 2012-09-14T07:50:25.483 回答