大数字比小数字难划分吗?

时间:2019-12-22 10:55:32

标签: c optimization types division alu

我为一,发现比较大数比小数更困难。

我想知道对于CPU是否相同。是否有任何硬件允许较小的数字比较大的数字被更快地划分。

例如

100 / (uint8_t) 255
100 / (char) 255
100 / (int) 255
100 / (int) 2147483647
100 / (long) 2147483647
100 / (long) 9223372036854775807

我可以想象除法过程需要更多的步骤才能得到更大的数字,因此需要ALU进行更多的说明。我不确定数据类型是否会对此产生任何影响?

这些数字的除法之间是否会有明显的区别? 如果有机会,用较小的数字进行除法通常会更好(这是任何形式的优化)吗?

1 个答案:

答案 0 :(得分:0)

这取决于内部实现。考虑以下朴素的二进制长除法:

struct div_t{
  int quot;
  int rem;
};

struct div_t div(int dividend, int divisor){
    _Bool dividend_is_negative = (dividend < 0),
        divisor_is_negative = (divisor < 0),
        result_is_negative = divisor_is_negative ^ dividend_is_negative;
    unsigned quotient =0, shift, shifted;
    //if (dividend_is_negative) dividend = -dividend;
    divisor ^= -divisor_is_negative;
    divisor += divisor_is_negative;
    //if (dividend_is_negative) dividend = -dividend;
    dividend ^= -dividend_is_negative;
    dividend += dividend_is_negative;
    shifted = divisor;
    //shift divisor so its MSB is same as dividend's - minimize loops
    //if no builtin clz, then shift divisor until its >= dividend
    //such as: while (shifted<dividend) shifted<<=1;
    shift = __builtin_clz(divisor)-__builtin_clz(dividend);
    //clamp shift to 0 to avoid undefined behavior
    shift &= -(shift > 0);
    shifted<<=shift;
    do{
        unsigned tmp;
        quotient <<=1;
        tmp = (unsigned long) (shifted <= dividend);
        quotient |= tmp;
        //if (tmp) dividend -= shifted;
      dividend -= shifted & -tmp;
        shifted >>=1;
    }while (shifted >= divisor);
    //if (result_is_negative) quotient=-quotient, dividend=-dividend;
    quotient ^= -result_is_negative;
    dividend ^= -result_is_negative;
    quotient += result_is_negative;
    dividend += result_is_negative;     
    return (struct div_t){quotient, dividend};
}

显然,对于较小的数字,在天真的实现中循环会更快终止。

如果整个回路在电路中展开并并行化,则将占用大量芯片空间,因为每个位都依赖于较高位。对于台式机,服务器和超级计算机而言,这通常是值得的,但对于移动,嵌入式或物联网则不那么值得。

许多可配置的芯片组(risc-v,j-core等)具有快速划分和缓慢划分的选项,而有些则完全忽略了它们,让编译器执行类似我的示例的事情。