You can’t do it in a completely portable way. Rather than dealing with int64_t
, let us consider int8_t
. The principle is almost exactly the same, but the numbers are much easier to deal with. I8_MAX
will be 127, and I8_MIN
will be -128. Negating I8_MIN
will give 128, and there is no way to store that in int8_t
.
Unless you have strong evidence that this is a bottleneck, then the right answer is:
constexpr int8_t negate(int8_t i) {
return (i==I8_MIN) ? I8_MAX : -i;
}
If you do have such evidence, then you will need to investigate some platform dependent code – perhaps a compiler intrinsic of some sort, perhaps some clever bit-twiddling which avoids a conditional jump.
Edit: Possible branchless bit-twiddling
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);
}
You would need to profile to determine whether that is actually faster. Another option would be to shift top_bit
into the carry bit, and use add-with-carry (adding a constant zero), or write it in assembler, and use an appropriate conditionally executed instruction.
0
solved Can not flip sign