获取整数的负号并将其存储为 char 的最佳方法是什么?

What is the best way to get integer's negative sign and store it as char?

如何获取整数的符号并将其存储在字符中?一种方式是:

int n = -5
char c;
if(n<0)
    c = '-';
else
    c = '+';

或者:

char c = n < 0 ? '-' : '+';

但是有没有不用条件的方法呢?

这会在 x86-64 上使用 gcc/clang 创建无分支代码:

void storeneg(int X, char *C)
{
    *C='+';
    *C += (X<0)*('-'-'+');
}

https://gcc.godbolt.org/z/yua1go

有最高效便携的方式,却没有获得任何美妆大奖

我们可以假设有符号整数的 MSB 总是设置为负数。这是一个 100% 可移植的假设,即使考虑到奇异的符号格式(一个人的补码,有符号的幅度)也是如此。因此,最快的方法是简单地从整数中屏蔽掉 MSB。

任何整数的 MSB 位于位置 CHAR_BIT * sizeof(n) - 1;。在典型的 32 位主流系统上,例如 8 * 4 - 1 = 31.

所以我们可以这样写一个函数:

_Bool is_signed (int n)
{
  const unsigned int sign_bit_n = CHAR_BIT * sizeof(n) - 1;
  return (_Bool) ((unsigned int)n >> sign_bit_n);
}

在 x86-64 gcc 9.1 (-O3) 上,这会产生非常高效的代码:

is_signed:
        mov     eax, edi
        shr     eax, 31
        ret

此方法的优点还在于,与 x < 0 等代码不同,它不会在移植时冒被翻译成 "branch if negative" 指令的风险。

完整示例:

#include <limits.h>
#include <stdio.h>

_Bool is_signed (int n)
{
  const unsigned int sign_bit_n = CHAR_BIT * sizeof(n) - 1;
  return (_Bool) ((unsigned int)n >> sign_bit_n);
}

int main (void)
{
  int n = -1;

  const char SIGNS[] = {' ', '-'};
  char sign = SIGNS[is_signed(n)];
  putchar(sign);
}

反汇编(x86-64 gcc 9.1 (-O3)):

is_signed:
        mov     eax, edi
        shr     eax, 31
        ret
main:
        sub     rsp, 8
        mov     rsi, QWORD PTR stdout[rip]
        mov     edi, 45
        call    _IO_putc
        xor     eax, eax
        add     rsp, 8
        ret
char c = 43 + signbit(n) * 2 ;
  • char 43'+'
  • char 45'-'
  • signbit(NEGATIVE INTEGER) 是 true,转换为 1

int signbit(int) 包含在 cmath in C++ and math.h in C