简体   繁体   中英

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

How to get an integer's sign and store it in a char? One way is:

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

Or:

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

But is there a way to do it without conditionals?

This creates branchless code with gcc/clang on x86-64:

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

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

There's the most efficient and portable way, but it doesn't win any beauty awards.

We can assume that the MSB of a signed integer is always set if it is negative. This is a 100% portable assumption even when taking exotic signedness formats in account (one's complement, signed magnitude). Therefore the fastest way is to simply mask out the MSB from the integer.

The MSB of any integer is found at location CHAR_BIT * sizeof(n) - 1; . On a typical 32 bit mainstream system, this would for example be 8 * 4 - 1 = 31.

So we can write a function like this:

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

On x86-64 gcc 9.1 (-O3), this results in very efficient code:

is_signed:
        mov     eax, edi
        shr     eax, 31
        ret

The advantage of this method is also that, unlike code such as x < 0 , it won't risk getting translated into "branch if negative" instructions when ported.

Complete example:

#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);
}

Disassembly (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 is '+'
  • char 45 is '-'
  • signbit(NEGATIVE INTEGER) is true , converted to 1

int signbit(int) is included in cmath in C++ and math.h in C

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM