验证C/C++;有符号右移是特定编译器的算术运算? 根据C/C++标准,C和C++中的>运算符不一定是符号数的算术移位。随着位右移,0(逻辑)或符号位(算术)的移入取决于编译器实现

验证C/C++;有符号右移是特定编译器的算术运算? 根据C/C++标准,C和C++中的>运算符不一定是符号数的算术移位。随着位右移,0(逻辑)或符号位(算术)的移入取决于编译器实现,c++,c,math,signed,C++,C,Math,Signed,对于实现带符号整数逻辑右移的编译器,此代码是否会在编译时断言(失败) #define COMPILE_TIME_ASSERT(EXP) \ typedef int CompileTimeAssertType##__LINE__[(EXP) ? 1 : -1] #define RIGHT_SHIFT_IS_ARITHMETIC \ ( (((signed int)-1)>>1) == ((signed int)-1) ) // SHR must be arithme

对于实现带符号整数逻辑右移的编译器,此代码是否会在编译时断言(失败)

#define COMPILE_TIME_ASSERT(EXP) \
    typedef int CompileTimeAssertType##__LINE__[(EXP) ? 1 : -1]

#define RIGHT_SHIFT_IS_ARITHMETIC \
    ( (((signed int)-1)>>1) == ((signed int)-1) )

// SHR must be arithmetic to use this code
COMPILE_TIME_ASSERT( RIGHT_SHIFT_IS_ARITHMETIC );

看起来不错!您还可以将编译器设置为发出汇编文件(或在调试器中加载已编译的程序),并查看它为
signed int i发出的操作码;i>>1,但这并不像您的解决方案那样是自动的


如果你发现一个编译器没有实现有符号数字的算术右移,我很想听听它。

为什么要断言?如果编译器的shift运算符不适合您的需要,您可以通过对结果进行符号扩展来优雅地纠正这种情况。而且,有时运行时已经足够好了。毕竟,编译器的优化器可以使编译时超出运行时:

template <typename Number>
inline Number shift_logical_right(Number value, size_t bits)
{
    static const bool shift_is_arithmetic = (Number(-1) >> 1) == Number(-1);
    const bool negative = value < 0;
    value >>= bits;
    if (!shift_is_arithmetic && negative) // sign extend
        value |= -(Number(1) << (sizeof(Number) * 8 - bits));
}

从您的各种评论中,您谈到了如何使用这个跨平台。确保编译器保证在为平台编译时,其编译时运算符的行为与运行时运算符相同

浮点数可以找到不同行为的示例。如果要返回int,编译器是否以单精度、双精度或扩展精度进行常量表达式计算?比如

constexpr int a = 41;
constexpr int b = (a / 7.5);
我想说的是,当您跨这么多不同的体系结构工作时,您应该确保编译器在运行时保证与编译时相同的行为

编译器完全有可能在内部对extend进行签名,但不会在目标上生成预期的操作码。确保的唯一方法是在运行时进行测试或查看程序集输出


看汇编输出还不是世界末日……有多少不同的平台?因为这对性能非常关键,所以只需做“工作”,查看5种不同体系结构的1-3行汇编程序输出。这并不是说你必须通过一个完整的组装输出(通常!)来找到你的产品线。这非常非常容易做到。

是的,我基本上希望它是自动的。。。查看操作码并不是这个要求的合理预期,因为我在其他团队可能在各种平台上使用的库中使用它。“表达式E1>>E2的结果是E1(解释为位模式)被移位到右E2位位置。即使E1表达式是有符号整数类型,右移位也是逻辑的(即,左边填充零)。”对于那些拥有使用逻辑移位的机器的人,失败的编译将如何处理?为什么您的软件不能在这样的机器/编译器上使用?无论有符号数字的右移是算术还是逻辑,编写代码使其工作不是更好吗?我通过位旋转使用无分支选择(BFS)。它需要一个算术转换才能工作。我将编译时断言(右移是算术);在BFS标题中。代码需要使用RIGHT\u SHIFT\u IS\u算术定义来选择传统路径或无分支路径。由于分支预测失误的惩罚,在PS3/XBOX360 CPU上使用无分支代码可能会大大加快速度。顺便说一句,在明确说明原因的编译时断言中失败的编译比让代码神秘地失败要好。。。基本上,它会说这些例程不受这个编译器(或CPU)的支持。阿迪萨克:一定要分析一下你从掩码添加技巧中得到的改进——在int单元上,我发现它只是有时比普通的旧cmp/bge有所改进,这取决于编译器如何处理周围的东西。
fsel
并不是绝对的巨大胜利。另一个建议:如果您无法找到在编译时执行此测试的好方法,那么在main()的顶部执行快速运行时测试并在运行时测试没有给出您想要的结果时使用信息性诊断/建议崩溃也没有什么错。它不如编译时错误好,但由于在测试中不能错过它,它避免了意外地在启用了错误代码的情况下发送的可能性。有很多代码(例如,使用掩码的无分支选择)仅在使用算术符号移位的编译器上使用才有意义。在它们上面优雅地模拟一个算术变换可能会比原始代码慢很多,因此消除了“优化”。这很公平。尽管如此,我的第二点仍然有效:不要依赖预处理器。请参阅update.static_assert()是C++11x,我们不能使用boost。但是我所做的检查并不依赖于预处理器,我可以做如下操作,但它比我编写的示例更难阅读和理解:
typedef int compiletimeassertarithmaticshift[(((有符号int)-1)>>1=((有符号int)-1))?1:-1]
constexpr int a = 41;
constexpr int b = (a / 7.5);