无符号模数:替代方法?

时间:2010-02-24 03:35:35

标签: c++ c optimization math

我需要优化这个非常小但功能不健全的功能。

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

    return a % b;
}

在你喊出“你不需要优化它”之前,请记住这个函数被称为整个程序生命周期的50%,因为它被称为最小测试用例基准测试的21495808次。

该功能已由编译器内联,因此请不要添加inline关键字。

12 个答案:

答案 0 :(得分:14)

这可以避免循环:

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

请注意,a和b都需要签名。

答案 1 :(得分:10)

这应该这样做:

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

测试匹配原始。限制是2 a > INT_MIN补充机器。

答案 2 :(得分:7)

使用〜:)

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

%的优先级高于-

当-a是b的倍数时,如果可以返回b而不是0,则可以保存一些操作

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

答案 3 :(得分:4)

由于循环版本似乎非常快,让我们尝试消除除法:)

unsigned umod(int a, unsigned b){
    while(a>0)a-=b;
    while(a<0)a+=b;
    return a;
}

答案 4 :(得分:2)

便携版,仍然只有一个分区,没有分支,也没有乘法:

unsigned umod(int a, unsigned b) {
    int rem = a % (int) b;
    return rem + (-(rem < 0) & b);
}

答案 5 :(得分:1)

在原始函数中,你可以在while循环完成后返回负数,从而跳过mod。这是相同的精神,用乘法代替循环 - 虽然可以使字符更少......

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的原始功能。

答案 6 :(得分:1)

a % b中,如果任何操作数为unsigned,则两者都转换为unsigned。这意味着如果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的答案,因为它更简单(或者可能不是?!)。这是记录。

答案 7 :(得分:1)

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

答案 8 :(得分:1)

这是一个适用于整个无符号无分支的范围,但它使用乘法和2个分区

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

答案 9 :(得分:0)

如果a和b都比int小得多,那么你可以在mod之前为每个值添加足够大的b倍。

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

当然,如果+(b * 256)可以溢出,这个技巧不起作用,但是对于这个代码我可以看到很多用途,你可以肯定它永远不会。

答案 10 :(得分:0)

除了while循环之外,不确定%操作是否可以作为一个整体优化,但优化可能发生在&amp;的值的模式上。湾

如果在21495808次执行操作。

如果传递小于b(a

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

如果对于至少80%的情况,b是2的幂,我们可以使用按位运算符,如

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

如果预计数字会小于该值,则会降低性能,因为我们需要验证b是否为2的幂[即使在使用相同的运算符之后]也是如此。

甚至实现abs(a)的功能也可以使用按位运算符进行优化,具有它们自身的局限性,但是比验证a是否&lt;更快。 0

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

如果你能探索,会有更多这样的事情。

答案 11 :(得分:0)

我首选的解决方案是mod两次。我没有在C / C ++或unsigned中尝试过这个,但我的测试用例在Java中工作:

((a % b) + b) % b

好处是没有分支和简单。缺点是双模式。我没有比较性能,但我的理解是这些分支会影响性能。