Неподписанный modulos: альтернативный подход?

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

unsigned umod(int a, unsigned b)
{
    while(a < 0)
        a += b;

    return a % b;
}

Перед выкриком, "Вы не должны оптимизировать его", имейте в виду, что эта функция вызвана 50% всего времени жизни программы, как это называют 21495808 раз для самого маленького сравнительного теста тестового сценария.

Функция уже встраивается компилятором, поэтому не предлагайте добавлять inline ключевое слово.

31
задан LiraNuna 24 February 2010 в 04:08
поделиться

12 ответов

Это должно сработать:

unsigned umod(int a, unsigned b)
{
    if (a < 0)
    {
        unsigned r = (-a % b);
        if (r)
            return b - r;
        else
            return 0;
    }
    else
        return a % b;
}

Протестировано на соответствие оригиналу. Ограничение состоит в том, что a> INT_MIN на машинах с дополнением до 2s.

10
ответ дан 27 November 2019 в 22:35
поделиться

Поскольку версия с циклом кажется довольно быстрой, давайте попробуем устранить разделение :)

unsigned umod(int a, unsigned b){
    while(a>0)a-=b;
    while(a<0)a+=b;
    return a;
}
4
ответ дан 27 November 2019 в 22:35
поделиться

В a% b , если какой-либо из операндов беззнаковый , оба преобразуются в беззнаковый . Это означает, что если a отрицательно, вы получите значение по модулю UINT_MAX + 1 вместо a . Если UINT_MAX + 1 без остатка делится на b , тогда все в порядке, и вы можете просто вернуть a% b . Если нет, вам нужно сделать по модулю в типе int .

unsigned int umod(int a, unsigned int b)
{
    int ret;
    if (a >= 0) return a % b;
    if (b > INT_MAX) return a + b;
    ret = a % (int)b;
    if (ret < 0) ret += b;
    return ret;
}

Правка : Обновлено, но вы должны использовать ответ caf, как это проще (а может и нет ?!). Это здесь для протокола.

1
ответ дан 27 November 2019 в 22:35
поделиться

Использование ~:)

unsigned umod(int a, unsigned b)
{
    if (a<0) return b-1-~a%b;
    return a%b;
}

% имеет более высокий приоритет, чем -

Если можно вернуть b вместо 0, когда -a кратно b, вы можете сохранить some ops

unsigned umod(int a, unsigned b)
{
    if (a<0) return b - (-a % b);
    return a%b;
}

слегка измененная версия :)

unsigned umod(int a, unsigned b)
{
return(a<0)?b-(-a%b):a%b;
}

Вот получившаяся сборка

1    .globl umod3
2       .type   umod3, @function
3    umod3:
4    .LFB3:
5       .cfi_startproc
6       testl   %edi, %edi
7       js      .L18
8       movl    %edi, %eax
9       xorl    %edx, %edx
10      divl    %esi
11      movl    %edx, %eax
12      ret
13      .p2align 4,,10
14      .p2align 3
15   .L18:
16      movl    %edi, %eax
17      xorl    %edx, %edx
18      negl    %eax
19      divl    %esi
20      subl    %edx, %esi
21      movl    %esi, %edx
22      movl    %edx, %eax
23      ret
7
ответ дан 27 November 2019 в 22:35
поделиться

Портативное издание, по-прежнему только с одним делением, без ветвления и умножения:

unsigned umod(int a, unsigned b) {
    int rem = a % (int) b;
    return rem + (-(rem < 0) & b);
}
2
ответ дан 27 November 2019 в 22:35
поделиться

В вашей оригинальной функции вы могли возвращаться после завершения цикла while для отрицательных чисел, тем самым пропуская моду. Это в том же духе, заменяя цикл умножением - хотя можно было бы сделать так, чтобы в нем было меньше символов...

unsigned int umod2(int a, unsigned int b)
{
    return (a < 0) ? a + ((-a/b)+1)*b : a % b;
}

Вот версия цикла:

unsigned int umod2_works(int a, unsigned int b)
{
    if (a < 0)
    {
        while (a < 0)
            a += b;
        return a;
    } else {
        return a % b;
    }
}

Обе были проверены на соответствие оригинальной функции OP.

1
ответ дан 27 November 2019 в 22:35
поделиться

Вот тот, который работает во всем диапазоне беззнаковых без ветвления, но использует умножение и 2 деления

unsigned umod(int a, unsigned b)
{
    return (a>0)*a%b+(a<0)*(b-1-~a%b);
}
1
ответ дан 27 November 2019 в 22:35
поделиться

Если a и b намного меньше int, то вы можете просто добавить достаточно большое кратное b к каждому значению перед модификацией.

unsigned umod(int a, unsigned b)
{
    return (unsigned)(a + (int)(b * 256)) % b;
}

Конечно, этот трюк не работает, если a + (b * 256) может переполниться, но для многих случаев использования этого кода, которые я вижу, можно быть уверенным, что этого никогда не произойдет.

0
ответ дан 27 November 2019 в 22:35
поделиться

Это позволяет избежать зацикливания:

int tmp = a % b;
if (tmp < 0) tmp += b;

Обратите внимание, что и a и b должны быть подписаны.

14
ответ дан 27 November 2019 в 22:35
поделиться
int temp;

temp= (a > 0)? ( a % b ) :   b -( (-a) % b ) ;

код ниже:

int main()
{
int a;
unsigned b;
int temp;
printf("please enter an int and a unsigned number\n");
scanf("%d",&a);
scanf("%u",&b);
modulus(a,b);
temp= (a > 0)? ( a % b ) :   b -( (-a) % b ) ;
printf("\n temp is %d", temp);
return 0;
}
void modulus(int x,unsigned y)
{
int c;
if(x>0)
{
c=x%y;
printf("\n%d\n",c);}
else
{
while(x<0)
x+=y;
printf("\n%d\n",x);}
}


./a.out
please enter an int and a unsigned number
-8 3

1

 temp is 1
1
ответ дан 27 November 2019 в 22:35
поделиться

Кроме цикла while, не уверен, можно ли оптимизировать операцию% в целом, но оптимизация может происходить по шаблону значений для a и b.

Если операция выполняется за эти 21495808 раз.

Если шансы передать значение a, которое меньше b (a

if ( abs(a) < b ) // not specifically the abs function, can be your own implementation.
    return 0;
else
    return a%b;

Если b является степенью 2 как минимум в 80% случаев, мы можем использовать побитовые операторы, как в

return ( abs(a) & (b-1) );

. Если ожидается, что числа будут меньше указанного, это снизит производительность, так как нам нужно проверьте, является ли b степенью 2 [даже после использования поразрядных операторов для того же самого] для всего.

Даже функциональность для достижения abs (a) может быть оптимизирована с помощью побитовых операторов с их собственными ограничениями, но это быстрее, чем проверка того, является ли a <0.

n = (a ^ (a >> 31)) - (a >> 31); // instead of n = a < 0 ? -a : a;

Если вы сможете изучить, таких вещей будет больше.

0
ответ дан 27 November 2019 в 22:35
поделиться

Моим предпочтительным решением является двойная модификация. Я не пробовал это в C/C++ или с unsigned, но мои тестовые примеры работают в Java:

((a % b) + b) % b

Преимущество - отсутствие ветвления и простота. Недостатком является двойная модификация. Я не сравнивал производительность, но, насколько я понимаю, в наши дни именно ветвление вредит производительности.

0
ответ дан 27 November 2019 в 22:35
поделиться
Другие вопросы по тегам:

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