Быстрый знак целого числа в C

В C есть знаковая функция:

int sign(int x)
{
    if(x > 0) return 1;
    if(x < 0) return -1;
    return 0;
}

К сожалению, стоимость сравнения очень высока, поэтому мне нужно изменить функцию, чтобы уменьшить количество сравнений.

Я попробовал следующее:

int sign(int x)
{
    int result;
    result = (-1)*(((unsigned int)x)>>31);

    if (x > 0) return 1;

    return result;
}

В этом случае я получаю только одно сравнение.

Есть ли способ избежать сравнения вообще?

EDIT возможный дубликат не дает ответа на вопрос, так как все ответы С++, использует сравнение (которое я должен избегать) или не возвращает -1, +1, 0.

+7
источник поделиться
5 ответов

Прежде всего, целочисленное сравнение очень дешево. Это разветвление, которое может быть дорогостоящим (из-за риска неправильных прогнозов отрасли).

Я проверил вашу функцию на блоке Sandy Bridge с помощью gcc 4.7.2, и он занимает около 1.2ns за вызов.

Ниже примерно на 25% быстрее, примерно на 0,9 нс за звонок:

int sign(int x) {
    return (x > 0) - (x < 0);
}

Код машины для вышеперечисленного полностью нераспространен:

_sign:
    xorl    %eax, %eax
    testl   %edi, %edi
    setg    %al
    shrl    $31, %edi
    subl    %edi, %eax
    ret

Следует отметить две вещи:

  • Базовый уровень производительности очень высок.
  • Устранение разветвления действительно улучшает производительность здесь, но не значительно.
+25
источник
int sign(int x)
{
    // assumes 32-bit int and 2s complement signed shifts work (implementation defined by C spec)
    return (x>>31) | ((unsigned)-x >> 31);
}

Первая часть (x>>32) дает -1 для отрицательных чисел и 0 для 0 или положительных чисел. Вторая часть дает вам 1, если x > 0 или равно INT_MIN, и 0 в противном случае. Или дает правильный ответ.

Там также канонический return (x > 0) - (x < 0);, но, к сожалению, большинство компиляторов будут использовать ветки для генерации кода для этого, даже если нет видимых ветвей. Вы можете попытаться вручную превратить его в нераспространяемый код как:

int sign(int x)
{
    // assumes 32-bit int/unsigned
    return ((unsigned)-x >> 31) - ((unsigned)x >> 31);
}

который, возможно, лучше, чем предыдущий, поскольку он не зависит от поведения, определенного реализацией, но имеет тонкую ошибку в том, что он вернет 0 для INT_MIN.

+3
источник
другие ответы

Связанные вопросы


Похожие вопросы

int sign(int x) {    
    return (x>>31)|(!!x);
}  
+3
источник
int i = -10;
if((i & 1 << 31) == 0x80000000)sign = 0;else sign = 1;
//sign 1 = -ve, sign 0 = -ve 
0
источник

Если s(x) - это функция, которая возвращает знаковый бит x (вы внедрили его ((unsigned int)x)>>31), вы можете каким-то образом объединить s(x) и s(-x). Вот таблица истинности:

x > 0: s (x) = 0; s (-x) = 1; ваша функция должна вернуть 1

x < 0: s (x) = 1; s (-x) = 0; ваша функция должна вернуть -1

x = 0: s (x) = 0; s (-x) = 0; ваша функция должна вернуть 0

Итак, вы можете комбинировать их следующим образом:

s(-x) - s(x)
0
источник

Посмотрите другие вопросы по метке или Задайте вопрос