Преобразование подписанного целого числа в беззнаковое короткое с отрицательными значениями, равными нулю в C

Я хотел бы знать, есть ли эффективный способ преобразования целого числа со знаком в короткое без знака, где отрицательные целые значения просто устанавливаются равными 0 в коротком без знака (в C ANSI). Я знаю, что это можно сделать с помощью простого оператора if, подобного следующему:

int val1;
unsigned short val2;

val1=-5; 
if(val1<0){
   val2=0;
}else{
   val2=(unsigned short) val1;
}

Это преобразование происходит очень часто в моей программе, и случай, когда val1 является отрицательным, очень редок, поэтому кажется излишним, чтобы этот оператор if выполнялся каждый раз.

Есть ли более эффективный способ сделать это преобразование?


person gtdevel    schedule 09.05.2014    source источник
comment
Если это необычный случай, предсказатель ветвления может быть достаточно хорошим (очевидно, в зависимости от вашей архитектуры).   -  person Emil Vikström    schedule 09.05.2014
comment
Я предлагаю вам записать наиболее часто встречающееся условие в if, а затем использовать else для менее часто встречающегося состояния.   -  person Don't You Worry Child    schedule 09.05.2014
comment
Также попробуйте val2 = val1 * (val1 > 0); и выполните измерения.   -  person pmg    schedule 09.05.2014
comment
Вы можете создать функцию inline (например, inline si2us(int si, unsigned short *us)) для обработки преобразования. Обратите внимание, что inline относится к версии C99 и может не реализовываться компилятором Microsoft Visual Studio C.   -  person Some programmer dude    schedule 09.05.2014
comment
@JoachimPileborg Я думаю, что это называется __inline в компиляторе C MSVC. Но в любом случае это не ANSI C.   -  person Fred Foo    schedule 09.05.2014
comment
@pmg Если бы это был ответ, я бы проголосовал за него. Я успешно использовал этот шаблон для оптимизации числового кода.   -  person Fred Foo    schedule 09.05.2014
comment
Эта проверка неизбежна, лучше использовать тернарный оператор ?: as val2 = val1‹0 ? 0 : (беззнаковое короткое) val1;   -  person Murukz-userm    schedule 09.05.2014


Ответы (3)


Одна вещь, которая часто работает, — это использование логических результатов (0 или 1) непосредственно в выражениях.

Вместо вашей конструкции if/else попробуйте

val2 = val1 * (val1 > 0);

И не забудьте измерить разные коды с разными параметрами компилятора, если вам нужна абсолютно лучшая производительность.

person pmg    schedule 09.05.2014
comment
Разве умножение не займет больше времени, чем условное выражение? Или это зависит от компилятора? - person gtdevel; 09.05.2014
comment
Это простое умножение на 0 или 1. Оператор не имеет ответвлений. Это может работать лучше, чем if; может и нет. Вам нужно измерить. Дело в том, что метод стоит попробовать. - person pmg; 09.05.2014
comment
Спасибо за чаевые. Я намереваюсь запустить это встроенное, так что это может измениться, но запуск его в Visual Studio 2 миллиона раз не показал значительных различий во времени вычислений на моем процессоре. Но, как я уже сказал, это может измениться, когда я встрою его, поэтому я буду иметь это в виду. Спасибо. - person gtdevel; 09.05.2014

Если вы используете gcc, вы можете использовать встроенную функцию, чтобы дать оптимизатору подсказку о вероятном результате целочисленного (или логического) выражения.

#define likely(x)      __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)

В следующем примере мы помечаем ветку как вероятную истину:

const char *home_dir ;

home_dir = getenv("HOME");
if (likely(home_dir))
    printf("home directory: %s\n", home_dir);
else
    perror("getenv");

Адаптировано к вашему коду:

#include <stdio.h>

#define likely(x)      __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)

int main(void)
{
    int val1;
    unsigned short val2;

    val1 = -5; 
    if (unlikely(val1 < 0)) {
        val2 = 0;
    } else {
        val2 = (unsigned short)val1;
    }
    return 0;
}

Другой способ с использованием заданной ширины и побитовых операторов:

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    int32_t val1;
    uint16_t val2;

    val1 = -5; 
    val2 = (0xffff ^ (val1 >> 31)) & val1;
    printf("%u\n", val2);
    return 0;
}
person David Ranieri    schedule 09.05.2014
comment
Я не использую gcc, но я посмотрю, есть ли в моем компиляторе что-то похожее на __builtin_expect. Я использую компилятор IAR для встроенных систем. - person gtdevel; 09.05.2014

Вот несколько различных способов сделать это:

val2 = (unsigned short)val1 * (1+(val1>>(sizeof(val1)*8-1)));
val2 = (unsigned short)val1 * (1^((val1>>(sizeof(val1)*8-1))&1));
val2 = (unsigned short)val1 * (1^((unsigned)val1>>(sizeof(val1)*8-1)));
val2 = (unsigned short)val1 * (1-((unsigned)val1>>(sizeof(val1)*8-1)));

Вы можете сделать его более общим, заменив 8 на CHAR_BIT (определено в limit.h).

Обратите внимание, что это не обязательно более эффективно, чем простой оператор if/else.

person barak manos    schedule 09.05.2014