Ответ 1
Да. C99 §7.20.6.2/2 гласит:
div
,ldiv
иlldiv
, вычисляют функцииnumer / denom
иnumer % denom
за одну операцию.
Есть ли конкретная причина использовать ldiv или div вместо '/' или '%' для разделения/модуля на две переменные?
Да. C99 §7.20.6.2/2 гласит:
div
,ldiv
иlldiv
, вычисляют функцииnumer / denom
иnumer % denom
за одну операцию.
Идея состоит в том, что результаты из/и% можно определить из одной инструкции DIV на процессоре. Таким образом, исторически, div() используется для обеспечения оптимизированного способа получения обоих.
Тем не менее, я обнаружил, что новые компиляторы в любом случае могут оптимизировать операцию/и% в один разделитель. Например, я видел эту оптимизацию на Microsoft Visual С++. В этих случаях div() действительно не дает преимущества и, по сути, может даже быть медленным, если задействован вызов.
Это должно быть быстрее, чем использование операторов /
и %
, если вы хотите одновременно вычислить как фактор, так и остаток.
Короткий ответ: не в современной среде.
Люди объяснили, почему преимущество 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 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 не говорится, как обойти фактор. Это связано с тем, что компилятор должен иметь возможность использовать реализацию машины, которая зависит от процессора. Существуют две различные реализации: - округление по направлению к бесконечности - округление до 0 ,
div(), однако, указан для последнего и поэтому переносится.