根据C / C标准(see this link),C和C中的>>运算符不一定是有符号数的算术移位 . 由于位向右移位,所以由编译器实现是0(逻辑)还是符号位(算术)移入 .
对于为有符号整数实现逻辑右移的编译器,此代码是否会在编译时对ASSERT(fail)起作用?
#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 );
3 回答
在我看来很好!您还可以将编译器设置为发出一个汇编文件(或在调试器中加载已编译的程序),并查看它为
signed int i; i >> 1;
发出的操作码,但这不是您自己的解决方案 .如果您发现编译器没有实现有符号数的算术右移,我想听听它 .
为什么断言?如果编译器的移位运算符不适合您的需要,您可以通过对结果进行符号扩展来优雅地纠正这种情况 . 此外,有时运行时间足够好 . 毕竟,编译器的优化器可以使编译时间超出运行时间:
static const bool
可以在编译时进行评估,因此如果shift_is_arithmetic
保证为true
,那么每个编译器都会消除整个if
子句并将const bool negative
构造为死代码 .注意:代码改编自Mono的
encode_sleb128
功能:here .Update
如果你真的想在没有算术移位的机器上中止编译,你最好还是不要依赖预处理器 . 您可以使用
static_assert
(或BOOST_STATIC_ASSERT
):从您的各种评论,您谈到使用这个跨平台 . 确保编译器保证在编译平台时,编译时运算符的行为与运行时运算符相同 .
可以使用浮点数找到不同行为的示例 . 如果要转换回int,您的编译器是以单精度,双精度还是扩展精度执行常量表达式数学运算?如
constexpr int a = 41; constexpr int b =(a / 7.5);
我所说的是,当你在这么多不同的架构上工作时,你应该确保你的编译器在运行时保证与编译时相同的行为 .
编译器完全有可能在内部进行符号扩展,但不会在目标上生成预期的操作码 . 唯一可以确定的方法是在运行时测试或查看汇编输出 .
看看装配输出并不是世界末日......有多少个不同的平台?由于这对性能至关重要,所以只需要为5种不同的架构查看1-3行汇编输出的“工作” . 这并不是说您必须潜入整个装配输出(通常!)才能找到您的 生产环境 线 . 这非常非常容易 .