不能翻转标志
Can not flip sign
我发现了一个奇怪的错误,当我尝试翻转数字 -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);
}
}
您无法以完全可移植的方式进行操作。让我们考虑 int8_t
而不是处理 int64_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;
}
如果您确实有这样的证据,那么您将需要调查一些依赖于平台的代码 - 也许是某种编译器内在代码,也许是一些聪明的 bit-twiddling 可以避免条件跳转。
编辑:可能无分支 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);
}
您需要分析以确定它是否真的更快。另一种选择是将 top_bit
移入进位位,并使用 add-with-carry(添加一个常量零),或将其写入汇编程序,并使用适当的条件执行指令。
我发现了一个奇怪的错误,当我尝试翻转数字 -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);
}
}
您无法以完全可移植的方式进行操作。让我们考虑 int8_t
而不是处理 int64_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;
}
如果您确实有这样的证据,那么您将需要调查一些依赖于平台的代码 - 也许是某种编译器内在代码,也许是一些聪明的 bit-twiddling 可以避免条件跳转。
编辑:可能无分支 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);
}
您需要分析以确定它是否真的更快。另一种选择是将 top_bit
移入进位位,并使用 add-with-carry(添加一个常量零),或将其写入汇编程序,并使用适当的条件执行指令。