【发布时间】:2010-12-30 18:39:24
【问题描述】:
是否有特定的理由使用 ldiv 或 div 而不是 '/' 或 '%' 来划分/取模两个变量?
【问题讨论】:
是否有特定的理由使用 ldiv 或 div 而不是 '/' 或 '%' 来划分/取模两个变量?
【问题讨论】:
是的。 C99 §7.20.6.2/2 说:
div、ldiv和lldiv函数在一次操作中计算numer / denom和numer % denom。
【讨论】:
这个想法是 / 和 % 的结果可以从处理器上的单个 DIV 指令确定。因此,从历史上看, div() 被用来提供一种优化的方式来获取两者。
但是,我发现较新的编译器无论如何都能够将 / 和 % 操作优化为一个除法。例如,我在 Microsoft Visual C++ 上看到过这种优化。在这些情况下,div() 确实没有任何优势,事实上,如果涉及调用,它甚至可能会更慢。
【讨论】:
don't even bother with that, your compiler's probably smarter than that anyway。现在我将在我的答案中添加斜体;)
如果您想同时计算商和余数,这应该比使用/ 和% 运算符要快。
【讨论】:
ldiv 系列函数来提高性能,那么您认为您的编译器不够聪明,无法使用 更便宜 的东西来优化您的 quotient+remainder 计算函数调用,或者您预计 ldiv 及其同类是编译器内在函数,而不是实际函数。
/ 和% 的代码时使用实现在ldiv 中使用的任何优化。但是为什么编译器要这样限制自己呢?
简短的回答:不是真的在现代环境中。
人们已经解释了为什么div 的好处很弱甚至不存在。
实际上更糟:div 和朋友介绍了类型耦合,这会损害良好实践(请参阅下面的缺点部分)。
正如在其他答案中所说,调用div 而不是/ 和% 很可能确保操作只在汇编级别完成一次。
但在大多数现代语境中:
div 的好处(如果有的话)通常可以忽略不计。/ 和% 时,现代编译器仍然可以通过仅生成一条除法指令来获取商和余数来执行正确的操作。 => div 没有实际好处。如果数字的确切类型(例如int 或long)是静态已知的(即,您的代码始终明确使用int 或long),则使用div 表示int,使用ldiv 表示长的没问题。
但是,如果您遵循小部分编程的良好做法,避免不必要的假设,您很快就会意识到使用div 和ldiv 将代码分别与int 或long 类型相关联。相反,/ 和 % 将自动调整为手头实际使用的任何类型,从而保持代码更简洁。
这在两种情况下尤其明显:
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 );
}
【讨论】:
好的,这是较旧的,但我只是偶然发现了这里。这里最重要的区别是: div() 的结果是定义的。 C标准没有说明如何舍入商。那是因为编译器应该能够使用取决于 CPU 的机器实现。存在两种不同的实现: - 向 -infinity 四舍五入 - 向 0 舍入 .
div(),然而被指定做后者,因此是可移植的。
【讨论】: