为什么在C/C++中使用div或ldiv?

She*_*raz 31 c c++

是否有特定的理由使用ldiv或div而不是'/'或'%'来划分/模拟两个变量?

Jam*_*lis 24

是.C99§7.20.6.2/ 2说:

div,ldiv以及lldiv,函数计算numer / denomnumer % denom在单一操作中.


Jon*_*ood 22

想法是来自/和%的结果可以从处理器上的单个DIV指令确定.因此,从历史上看,div()用于提供获得两者的优化方法.

但是,我发现新的编译器能够将/和%运算优化为单个除法.例如,我在Microsoft Visual C++上看到了这种优化.在这些情况下,div()实际上不提供优势,事实上,如果涉及呼叫,甚至可能会很慢.

  • 我认为任何足够智能的编译器都可以将连续的%和/操作优化为单个处理器指令,它也足够聪明,可以内联对div()的调用.:/ (7认同)
  • 对于一个明确的`甚至不打扰它的+1,你的编译器可能比那更聪明.现在我只是去添加斜体到我的答案;) (4认同)

Fré*_*idi 9

如果你想同时计算商和余数,那应该比使用/%运算符更快.

  • @Stuart Golodetz:要求X必须比Y快,这与强制Y必须*比*慢*相同,这不是一个好的业务.确保它更快的唯一方法是禁止优化器在看到计算`/`和`%`的代码时,使用实现在`ldiv`中使用的任何优化.但为什么编译器会限制自己呢? (6认同)

Sté*_*hon 5

简短的回答:在现代环境中并非如此。

人们已经解释了为什么收益div很弱甚至不存在。 实际上更糟糕:div朋友们引入了类型耦合,这损害了良好的实践(请参阅下面的缺点部分)。

优点:可能没有

正如其他答案中所述,调用div而不是/and%最有可能确保该操作仅在程序集级别完成一次。

但在大多数现代环境中:

  1. CPU 具有非常好的优化数学运算,除了在计算的最内层循环中执行了无数次之外,重复操作所造成的任何性能影响可能都比其他性能影响(如周围的其他代码、缓存未命中等)小得多。=> 的好处div(如果有的话)通常可以忽略不计。
  2. 即使在使用/现代%编译器时,无论如何,通过仅生成一个除法指令来获取商和余数,也可以做正确的事情。=> 没有实际好处div

缺点:div 和朋友将你的代码绑定到特定类型

如果数字的确切类型(例如intlong)是静态已知的(也就是说,您的代码始终显式使用 int 或 long),则使用divforintldivfor long 是可以的。

但是,如果您遵循小部分编程的良好实践,避免不必要的假设,您很快就会意识到使用div并将ldiv代码分别与类型int或联系起来long。相反,/并且%会自动调整到当前情况下实际使用的任何类型,从而保持代码简洁。

这在两种情况下尤其明显:

  • 你用来typedef抽象实际类型——div即使在 C 中也是笨拙的!
  • 你使用模板来抽象出实际类型——div打败了模板。

例子

下面的示例显示,使用 '/' 和 '%' 的代码干净、简单,并且与 int、long、long long 或其他任何东西无关,而使用divand 的代码则变得笨拙。

Testing with int
my_math_func_div_WRONG  says 6
my_math_func_OVERKILL   says 6  // Works but overkill cast to long
my_math_func_GOOD   says 6      // No div no headache.

Testing with int in long type
my_math_func_div_WRONG  says 6
my_math_func_OVERKILL   says 6  // Works but overkill cast to long
my_math_func_GOOD   says 6      // No div no headache.

Testing with actual long
my_math_func_div_WRONG  says 70503280   // FAIL
my_math_func_OVERKILL   says 500000006
my_math_func_GOOD   says 500000006      // No div no headache.
Run Code Online (Sandbox Code Playgroud)

源代码:

#include <iostream>

// '/' and '%' are smart about type.
// This code is simple and will work with int, long, longlong, char, whatever.
template<typename T>
T my_math_func_GOOD( T number )
{
    T quotient = number / 10;
    T remainder = number % 10;
    // do something
    return quotient + remainder;
}

// div and friends are not smart about type.
// How do you write code smart about type with them ?

// Plus adds dependency on C's stdlib.
#include <stdlib.h>
template<typename T>
T my_math_func_div_WRONG( T number )
{
    // This will always downcast to int. Defeats purpose of template.
    div_t result = div( number, 10 );
    T quotient = result.quot;
    T remainder = result.rem;
    // do something
    return quotient + remainder;
}

template<typename T>
T my_math_func_OVERKILL( T number )
{
    // This will always cast to long (up from int, OVERKILL, possibly down from long long, FAIL). Defeats purpose of template.
    ldiv_t result = ldiv( number, 10 );
    T quotient = result.quot;
    T remainder = result.rem;
    // do something
    return quotient + remainder;
}

template<typename T>
void my_math_func_test( T number )
{
    T n;
    n = my_math_func_div_WRONG( number );
    std::cout << "my_math_func_div_WRONG\tsays " << n << std::endl; // writes 6
    n = my_math_func_OVERKILL( number );
    std::cout << "my_math_func_OVERKILL\tsays " << n << std::endl; // writes 6
    n = my_math_func_GOOD( number );
    std::cout << "my_math_func_GOOD\tsays " << n << std::endl; // writes 6
}

// C99 allows absence of int argc, char **argv
int main()
{
    std::cout << std::endl << "Testing with int" << std::endl;
    my_math_func_test<int>( 42 );
    std::cout << std::endl << "Testing with int in long type" << std::endl;
    my_math_func_test<long>( 42 );
    std::cout << std::endl << "Testing with actual long" << std::endl;
    my_math_func_test<long>( 5000000042 );
    // std::cout << std::endl << "Testing with long long" << std::endl;
    // my_math_func_test<long long>( 50000000000000000042 );
}
Run Code Online (Sandbox Code Playgroud)