Каков наилучший способ получить отрицательный знак целого числа и сохранить его как символ? - PullRequest
2 голосов
/ 19 июня 2019

Как получить целочисленный знак и сохранить его в символе?Один из способов:

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

Или:

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

Но есть ли способ сделать это без условий?

Ответы [ 3 ]

5 голосов
/ 19 июня 2019

Существует самый эффективный и портативный способ, но он не получает никаких наград за красоту.

Можно предположить, что 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, он не рискует быть переведенным в "ветвящиеся, если отрицательные" инструкции при переносе.

Полный пример:

#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
5 голосов
/ 19 июня 2019

Это создает код без ответвлений с помощью gcc / clang на x86-64:

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

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

1 голос
/ 19 июня 2019
char c = 43 + signbit(n) * 2 ;
  • char 43 равно '+'
  • char 45 равно '-'
  • signbit (ОТРИЦАТЕЛЬНЫЙ ИНТЕГЕР) равен true, преобразован в 1

int signbit(int) включено в cmath в C ++ и math.h в C

...