【问题标题】:Why use div or ldiv in C/C++?为什么在 C/C++ 中使用 div 或 ldiv?
【发布时间】:2010-12-30 18:39:24
【问题描述】:

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

【问题讨论】:

标签: c++ c


【解决方案1】:

是的。 C99 §7.20.6.2/2 说:

divldivlldiv 函数在一次操作中计算 numer / denomnumer % denom

【讨论】:

【解决方案2】:

这个想法是 / 和 % 的结果可以从处理器上的单个 DIV 指令确定。因此,从历史上看, div() 被用来提供一种优化的方式来获取两者。

但是,我发现较新的编译器无论如何都能够将 / 和 % 操作优化为一个除法。例如,我在 Microsoft Visual C++ 上看到过这种优化。在这些情况下,div() 确实没有任何优势,事实上,如果涉及调用,它甚至可能会更慢。

【讨论】:

  • +1 表示明确的don't even bother with that, your compiler's probably smarter than that anyway。现在我将在我的答案中添加斜体;)
  • 我认为任何足够聪明的编译器可以将连续的 % 和 / 操作优化到单个处理器指令中,也足够聪明以内联对 div() 的调用。 ://
  • 除非您将某些编译器选项设置为 not 内联标准库调用。为什么我认为存在这样的选择......也许是从我以前用 C 编程的那一天开始的?
  • @Karl:仅仅因为编译器足够聪明,可以内联调用,并不一定意味着它已配置,甚至能够在所有情况下进行配置。无论如何,我以为我已经用“may”和“if”来涵盖了这一点。
【解决方案3】:

如果您想同时计算商和余数,这应该比使用/% 运算符要快。

【讨论】:

  • édéric:当你说“supposed to be faster”时,是不是暗示它有时不是?
  • @Stuart,绝对是,请参阅@Jonathan Wood 的回答和我随后的评论。基本上,如果您正在考虑使用 ldiv 系列函数来提高性能,那么您认为您的编译器不够聪明,无法使用 更便宜 的东西来优化您的 quotient+remainder 计算函数调用,或者您预计 ldiv 及其同类是编译器内在函数,而不是实际函数。
  • @Stuart Golodetz:要求 X 必须比 Y 快与要求 Y 必须比 X 是一样的,这不是一个好生意.确保它更快的唯一方法是禁止优化器在查看计算/% 的代码时使用实现在ldiv 中使用的任何优化。但是为什么编译器要这样限制自己呢?
  • édéric,@Steve:谢谢两位,有道理:)
【解决方案4】:

简短的回答:不是真的在现代环境中。

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

优点:可能没有

正如在其他答案中所说,调用div 而不是/% 很可能确保操作只在汇编级别完成一次。

但在大多数现代语境中:

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

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

如果数字的确切类型(例如intlong)是静态已知的(即,您的代码始终明确使用int 或long),则使用div 表示int,使用ldiv 表示长的没问题。

但是,如果您遵循小部分编程的良好做法,避免不必要的假设,您很快就会意识到使用divldiv 将代码分别与intlong 类型相关联。相反,/% 将自动调整为手头实际使用的任何类型,从而保持代码更简洁。

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

  • 您使用 typedef 抽象出实际类型 -- div 即使在 C 语言中也很笨拙!
  • 您使用模板来抽象出实际类型——div 击败了模板。

示例

下面的示例显示使用“/”和“%”的代码干净、简单,并且不依赖于 int、long、long long 或其他任何内容,而使用 div 和朋友的代码变得笨拙。

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.

源代码:

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

【讨论】:

    【解决方案5】:

    好的,这是较旧的,但我只是偶然发现了这里。这里最重要的区别是: div() 的结果是定义的。 C标准没有说明如何舍入商。那是因为编译器应该能够使用取决于 CPU 的机器实现。存在两种不同的实现: - 向 -infinity 四舍五入 - 向 0 舍入 .

    div(),然而被指定做后者,因此是可移植的。

    【讨论】:

    • 你能提供一个链接来证明这一点吗?
    • C99 起,“整数除法时,/ 运算符的结果是去掉任何小数部分的代数商”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 2015-06-08
    • 2014-01-04
    相关资源
    最近更新 更多