不能翻转标志

时间:2018-03-04 15:35:10

标签: c++ macos numbers int signed

当我试图翻转数字-9223372036854775808的符号时,我发现了一个奇怪的错误,它什么也没做。 我得到了相同的数字,或者至少是调试器给我看的那个。 有没有办法在没有分支的情况下解决这个问题?

#define I64_MAX  9223372036854775807LL
#define I64_MIN  (-I64_MAX-1) 
// -9223372036854775808 (can not be a constant in code as it will turn to ull)

using i64 = long long int;

int main()
{
 i64 i = I64_MIN;
 i = -i;
 printf("%lld",i);
 return 0;
}

与i32,i16,i8相同。

<小时/> 修改
当前修复:

// use template??
c8* szi32(i32 num,c8* in)
{
    u32 number = S(u32,num);
    if(num < 0)
    {
        in[0] = '-';
        return SerializeU32(number,&in[1]);
    }
    else
    {
        return SerializeU32(number,in);
    }
} 

1 个答案:

答案 0 :(得分:1)

你不能以完全可移植的方式做到这一点。我们不考虑处理int64_t,而是考虑int8_t。原则几乎完全相同,但数字更容易处理。 I8_MAX将为127,I8_MIN将为-128。否定I8_MIN将提供128,并且无法将其存储在int8_t中。

除非你证明这是一个瓶颈,否则正确答案是:

constexpr int8_t negate(int8_t i) {
    return (i==I8_MIN) ? I8_MAX : -i;
}

如果你确实有这样的证据,那么你需要调查一些与平台相关的代码 - 也许是某种类型的编译器内部代码,也许是一些巧妙的比特错误,它可以避免条件跳转。

编辑:可能的无分支比特 -

constexpr int8_t negate(int8_t i) {
    const auto ui = static_cast<uint8_t>(i); 
    // This will calculate the two's complement negative of ui.
    const uint8_t minus_ui = ~ui+1;
    // This will have the top bit set if, and only if, i was I8_MIN
    const uint8_t top_bit = ui & minus_ui;
    // Need to get top_bit into the 1 bit.  Either use a compiler intrinsic rotate:
    const int8_t bottom_bit = static_cast<int8_t>(rotate_left(top_bit)) & 1;
    // -or- hope that your implementation does something sensible when you
    // shift a negative number (most do).
    const int8_t arithmetic_shifted = static_cast<int8_t>(top_bit) >> 7;
    const int8_t bottom_bit = arithmetic_shifted & 1;
    // Either way, at this point, bottom_bit is 1 if and only if i was
    // I8_MIN, otherwise it is zero.
    return -(i+bottom_bit);
}

您需要进行分析以确定实际上是否更快。另一个选择是将top_bit移入进位,并使用add-with-carry(添加常数为零),或者在汇编程序中写入,并使用适当的有条件执行指令。