标准div()函数返回div_t结构作为参数,例如:

/* div example */
#include <stdio.h>      /* printf */
#include <stdlib.h>     /* div, div_t */

int main ()
{
  div_t divresult;
  divresult = div (38,5);
  printf ("38 div 5 => %d, remainder %d.\n", divresult.quot, divresult.rem);
  return 0;
}


我的情况有些不同。我有这个

#define NUM_ELTS 21433
int main ()
{
  unsigned int quotients[NUM_ELTS];
  unsigned int remainders[NUM_ELTS];
  int i;

  for(i=0;i<NUM_ELTS;i++) {
      divide_single_instruction(&quotient[i],&reminder[i]);
  }
}


我知道除法汇编语言可以在单个指令中完成所有操作,因此我需要在此处执行同样的操作以节省CPU周期,这是笨拙地将商从EAX移出,将提醒从EDX移至存储我的数组的存储位置。如何在我的C代码中不包含asm {}或SSE内部函数的情况下完成此操作?它必须是便携式的。

最佳答案

由于您是在原位写入数组(用商和余数替换分子和分母),因此在写入数组之前,应将结果存储到临时变量中。

void foo (unsigned *num, unsigned *den, int n) {
    int i;
    for(i=0;i<n;i++) {
        unsigned q = num[i]/den[i], r = num[i]%den[i];
        num[i] = q, den[i] = r;
    }
}


产生这个主循环组件

.L5:
        movl    (%rdi,%rcx,4), %eax
        xorl    %edx, %edx
        divl    (%rsi,%rcx,4)
        movl    %eax, (%rdi,%rcx,4)
        movl    %edx, (%rsi,%rcx,4)
        addq    $1, %rcx
        cmpl    %ecx, %r8d
        jg      .L5


在一些更复杂的情况下,初次使用时会节省商和余数。例如,在通过试验划分测试素数时,您经常会看到这样的循环

for (p = 3; p <= n/p; p += 2)
    if (!(n % p)) return 0;


事实证明GCC does not use the remainder from the first division并因此执行了两次除法指令,这是不必要的。为了解决这个问题,您可以在完成第一个除法时保存其余部分,如下所示:

for (p = 3, q=n/p, r=n%p; p <= q; p += 2, q = n/p, r=n%p)
    if (!r) return 0;


这样可以将结果加快两倍。

因此,一般而言,GCC会做得很好,特别是如果您在第一次计算商和余数时保存了商和余数。

10-08 15:12