2010-12-30 8 views

Respuesta

24

Sí. C99 §7.20.6.2/2 dice:

El div, ldiv y lldiv, funciones y calcular numer/denomnumer % denom en una sola operación.

+0

Gracias, http://www.cppreference.com/wiki/numeric/c/div me lo aclaró también. faltaba el cociente y el resto. – Sheraz

9

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.

+1

édéric: Cuando dices "* supposed * ser más rápido", ¿está la implicación de que a veces no es así? –

+1

@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. –

+6

@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? –

21

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.

+3

+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;) –

+7

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() . :/ –

+1

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? –

2

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:

  1. 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.
  2. 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 para div.

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

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.