resueltos regla por paso para numero natural ejercicios ejemplos divisor divisiones dividir dividendo decimales con como c++ visual-c++ assembly x86 division

c++ - regla - ¿Por qué se emite ese código complejo para dividir un entero con signo por una potencia de dos?



divisiones con decimales en dividendo y divisor ejercicios resueltos (3)

Del estándar C:

Cuando los enteros se dividen, el resultado del operador / es el cociente algebraico con cualquier parte fraccional descartada.105) Si el cociente a / b es representable, la expresión (a / b) * b + a% b será igual a; de lo contrario, el comportamiento de a / by a% b no está definido.

No es difícil pensar en ejemplos donde los valores negativos para una no siguen esta regla con un cambio aritmético puro. P.ej

(-8191) / 4096 -> -1 (-8191) % 4096 -> -4095

que satisface la ecuación, mientras que

(-8191) >> 12 -> -2 (assuming arithmetic shifting)

no es división con truncamiento, y por lo tanto -2 * 4096 - 4095 ciertamente no es igual a -8191.

Tenga en cuenta que el desplazamiento de los números negativos en realidad está definido por la implementación, por lo que la expresión C (-8191) >> 12 no tiene un resultado generalmente correcto según el estándar.

Cuando compilo este código con VC ++ 10:

DWORD ran = rand(); return ran / 4096;

Obtengo este desmontaje:

299: { 300: DWORD ran = rand(); 00403940 call dword ptr [__imp__rand (4050C0h)] 301: return ran / 4096; 00403946 shr eax,0Ch 302: } 00403949 ret

que es limpio y conciso y reemplazó una división por una potencia de dos con un cambio de sentido lógico.

Sin embargo, cuando compilo este código:

int ran = rand(); return ran / 4096;

Obtengo este desmontaje:

299: { 300: int ran = rand(); 00403940 call dword ptr [__imp__rand (4050C0h)] 301: return ran / 4096; 00403946 cdq 00403947 and edx,0FFFh 0040394D add eax,edx 0040394F sar eax,0Ch 302: } 00403952 ret

que realiza algunas manipulaciones antes de hacer un cambio aritmético correcto.

¿Cuál es la necesidad de esas manipulaciones adicionales? ¿Por qué un cambio aritmético no es suficiente?


La razón es que la división sin signo por 2 ^ n se puede implementar de manera muy simple, mientras que la división con signo es algo más compleja.

unsigned int u; int v;

u / 4096 es equivalente a u >> 12 para todos los valores posibles de u .

v / 4096 NO es equivalente a v >> 12 - se descompone cuando v < 0 , ya que la dirección de redondeo es diferente para el cambio frente a la división cuando se trata de números negativos.


las "manipulaciones adicionales" compensan el hecho de que el desplazamiento aritmético hacia la derecha redondea el resultado hacia el infinito negativo, mientras que la división redondea el resultado hacia cero.

Por ejemplo, -1 >> 1 es -1 , mientras que -1/2 es 0 .