开发者

Why use div or ldiv in C/C++?

开发者 https://www.devze.com 2023-02-02 08:21 出处:网络
Is there a specific reason to use ldiv or div instead of \'/\' o开发者_如何学Gor \'%\' to divide/modulus two variables? Yes.C99 §7.20.6.2/2 says:

Is there a specific reason to use ldiv or div instead of '/' o开发者_如何学Gor '%' to divide/modulus two variables?


Yes. C99 §7.20.6.2/2 says:

The div, ldiv, and lldiv, functions compute numer / denom and numer % denom in a single operation.


The idea is that results from / and % can be determined from a single DIV instruction on the processor. So, historically, div() is used to provide an optimized way to get both.

However, I have found that newer compilers are able to optimize a / and % operation into a single divide anyway. For example, I've seen this optimization on Microsoft Visual C++. In these cases, div() really doesn't provide an advantage and, in fact, may even be slower if a call is involved.


That's supposed to be faster than using the / and % operators if you want to compute both the quotient and the remainder at the same time.


Short answer : not really in a modern environment.

People have explained why benefit of div is weak or even non-existent. It's actually even worse : div and friends introduce type coupling which hurts good practice (see drawback section below).

Advantage : probably none

As said in other answers, calling div instead of / and % most probably ensures that the operation is only done once at assembly level.

But in most modern contexts:

  1. The CPU has so well optimized mathematical operations that except in the innermost loop of a computation done zillion of times, any performance hit by repeating the operation is probably way smaller than other performance hits (like other code around, cache miss, etc.). => Benefit of div, if any, is generally negligible.
  2. Even when using / and % modern compilers do the right thing anyway by generating only one division instruction fetching quotient and remainder from that. => No actual benefit for div.

Drawback : div and friends tie your code to a specific type

If the exact type of the numbers (e.g. int or long) are statically known (that is, your code explicitly uses int or long always), using div for int and ldiv for long is okay.

But if you follow the good practice of programming in small parts avoiding unneeded assumptions, you quickly realize that using div and ldiv ties the code to types int or long respectively. On the contrary, / and % will automatically adjust to whatever type is actually used in the case at hand, keeping code cleaner.

This is especially visible in two cases :

  • you use typedef to abstract away actual types -- div is clumsy even in C !
  • you use templates to abstract away actual types -- div defeats templates.

Example

The sample below shows that code using '/' and '%' is clean, simple, and not tied to int, long, long long or whatever, whereas code using div and friends becomes clumsy.

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.

Source code:

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


Okay, this is older, but I just stumbled here. The most important difference here is: The result of div() is defined. The C standard does not say how to round the quotient. That is because the compiler should be able to use the machine's implementation which depends on the CPU. Two different implementations exist: - rounding towards -infinity - rounding towards 0 .

div(), however is specified to do the latter, and is therefore portable.

0

精彩评论

暂无评论...
验证码 换一张
取 消