¿Existe alguna razón específica para usar ldiv o div en lugar de '/' o '%' para dividir/modular dos variables?¿Por qué usar div o ldiv en C/C++?
Respuesta
Sí. C99 §7.20.6.2/2 dice:
El
div
,ldiv
ylldiv
, funciones y calcularnumer/denom
numer % denom
en una sola operación.
Eso es supone ser más rápido que el uso de los /
y %
operadores si desea calcular tanto el cociente y el resto al mismo tiempo.
édéric: Cuando dices "* supposed * ser más rápido", ¿está la implicación de que a veces no es así? –
@Stuart, absolutamente, ver la respuesta de @Jonathan Wood y mi comentario posterior. Básicamente, si estás considerando utilizar la familia de funciones 'ldiv' para aumentar el rendimiento, estás pensando que tu compilador no es lo suficientemente inteligente como para optimizar tu cálculo' cociente + resto' con algo * más barato * que una llamada a función , o anticipa que 'ldiv' y su tipo son intrínsecos al compilador y no funciones reales. –
@Stuart Golodetz: Obligar a que X debe ser más rápido que Y es lo mismo que exigir que Y debe ser * más lento * que X, lo cual no es un buen negocio para entrar. La única manera de garantizar que sea más rápido sería prohibir que el optimizador, al ver el código que calcula tanto '/' como '%', use las optimizaciones que la implementación use en 'ldiv'. ¿Pero por qué un compilador debería restringirse a sí mismo? –
La idea es que los resultados de/y% se pueden determinar a partir de una única instrucción DIV en el procesador. Entonces, históricamente, div() se usa para proporcionar una forma optimizada de obtener ambos.
Sin embargo, he encontrado que los compiladores más nuevos son capaces de optimizar el funcionamiento de/y% en una sola división de todos modos. Por ejemplo, he visto esta optimización en Microsoft Visual C++. En estos casos, div() realmente no proporciona una ventaja y, de hecho, incluso puede ser lento si se trata de una llamada.
+1 por un explícito 'ni siquiera te molestes con eso, tu compilador probablemente sea más inteligente que eso de todos modos '. Ahora voy a agregar cursivas a mi respuesta;) –
Creo que cualquier compilador que sea lo suficientemente inteligente como para optimizar el% y las operaciones consecutivas en una sola instrucción de procesador, también es lo suficientemente inteligente como para alinear una llamada a div() . :/ –
A menos que configure alguna opción del compilador para las llamadas a biblioteca estándar en línea * not *. ¿Por qué creo que existe una opción así ... tal vez desde el día en que solía programar en C? –
Respuesta corta: no realmente en un entorno moderno.
Las personas han explicado por qué el beneficio de div
es débil o incluso inexistente. En realidad es aún peor: div
y sus amigos introducen el tipo de acoplamiento que perjudica las buenas prácticas (consulte la sección de reintegros a continuación).
Ventaja: es probable que ninguno
Como se dijo en otras respuestas, llamando div
en lugar de /
y %
muy probablemente asegura que la operación sólo se realiza una vez al nivel de ensamblado.
Pero en la mayoría de los contextos modernos:
- La CPU ha tan bien optimizados operaciones matemáticas que, salvo en el bucle más interno de un cálculo hecho trillón de veces, ningún impacto en el rendimiento mediante la repetición de la operación es probablemente manera más pequeño que otros éxitos de rendimiento (como otro código alrededor, error de caché, etc.). => El beneficio de
div
, si lo hay, es generalmente insignificante. - Incluso cuando se usan
/
y%
los compiladores modernos hacen lo correcto de todos modos al generar solo una instrucción de división obteniendo el cociente y el resto de eso. => Sin beneficio real paradiv
.
Desventaja: div y amigos atar su código a un tipo específico
Si el tipo exacto de los números (por ejemplo int
o long
) son conocidos de forma estática (es decir, el código utiliza explícitamente int o largo siempre), usando div
para int
y ldiv
por mucho tiempo está bien.
Pero si sigue la buena práctica de programación en partes pequeñas para evitar supuestos que no sean necesarios, se da cuenta rápidamente que el uso de div
y ldiv
lazos del código de tipos int
o long
respectivamente. Por el contrario, /
y %
se ajustarán automáticamente a cualquier tipo que se use realmente en el caso en cuestión, manteniendo el código más limpio.
Esto es especialmente visible en dos casos:
- utiliza
typedef
a los tipos abstractos de distancia reales -div
es torpe incluso en C! - utiliza plantillas para abstraer tipos reales ausentes -
div
derrotas de plantillas.
Ejemplo
El ejemplo siguiente muestra que el código usando '/' y '%' es limpio, simple, y no están vinculados a int, largo, largo, largo o lo que sea, mientras que el código usando div
y amigos se convierte torpe.
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.
Código Fuente:
#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);
}
Vale, esto es más viejo, pero yo sólo tropezó aquí. La diferencia más importante aquí es: se define el resultado de div(). El estándar C no dice cómo redondear el cociente. Eso es porque el compilador debería poder usar la implementación de la máquina, que depende de la CPU. Existen dos implementaciones diferentes: - redondeado hacia -infinity - redondeando hacia 0 .
div(), sin embargo, está especificado para hacer esto último y, por lo tanto, es portátil.
- 1. ¿Qué es call/cc?
- 2. ¿Qué significa CC? = En un Makefile?
- 3. ¿Por qué debería usar un contenedor div en HTML?
- 4. En makefiles, ¿qué significan CC y LD?
- 5. ¿Por qué usar Context.MODE_PRIVATE o Context.MODE_WRITABLE?
- 6. Por qué usar (function() {....}());
- 7. Detalles de call/cc
- 8. ¿Por qué no podemos usar el direccionamiento directo en el código c o C++?
- 9. ¿Por qué usar Mono?
- 10. ¿Por qué usar mysqli_close()?
- 11. Por qué usar NSObjectController
- 12. patrones "call-cc" en Scala?
- 13. ¿Es posible usar call/cc para implementar la recursión?
- 14. ¿Cómo cambian DP y CC en Piet?
- 15. ¿Por qué algunos proyectos eligen la extensión para los archivos fuente .cc en C++?
- 16. ¿Qué usar, XMP o RDF?
- 17. ¿Por qué debería usar enchufes que no bloquean o bloquean?
- 18. IObservable vs eventos simples o ¿Por qué debería usar IObservable?
- 19. Excepciones a C++: ¿Por qué usar o extender std :: exception?
- 20. ¿Por qué usar hex?
- 21. ¿Por qué usar @PostConstruct?
- 22. ¿Por qué querría uno usar ruby sobre python o viceversa?
- 23. ¿Por qué usar Eventos?
- 24. Qué usar: executeUpdate() o execute()?
- 25. ¿Por qué usar document.write?
- 26. Por qué usar scala.collection.immutable.Stack
- 27. ¿Por qué usar java.io.Console?
- 28. ¿Por qué usar DialogFragment?
- 29. ¿Por qué usar NSAutoreleasePool?
- 30. ¿Por qué usar NSFetchedResultsController?
Gracias, http://www.cppreference.com/wiki/numeric/c/div me lo aclaró también. faltaba el cociente y el resto. – Sheraz