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