Pourquoi un code complexe est-il émis pour diviser un entier signé par une puissance de deux?

Lorsque je comstack ce code avec VC ++ 10:

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

Je reçois ce déassembly:

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

qui est propre et concis et a remplacé une division par une puissance de deux avec un changement logique logique.

Pourtant, quand je comstack ce code:

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

Je reçois ce déassembly:

 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 

qui effectue des manipulations avant de faire un décalage arithmétique correct.

Quel est le besoin de ces manipulations supplémentaires? Pourquoi un décalage arithmétique est-il insuffisant?

La raison en est que la division non signée par 2 ^ n peut être implémentée très simplement, alors que la division signée est un peu plus complexe.

 unsigned int u; int v; 

u / 4096 est équivalent à u >> 12 pour toutes les valeurs possibles de u .

v / 4096 N’EST PAS équivalent à v >> 12 – il se décompose lorsque v < 0 , car la direction d'arrondi est différente pour le décalage par rapport à la division lorsque des nombres négatifs sont impliqués.

les “manipulations supplémentaires” compensent le fait que le décalage arithmétique à droite arrondit le résultat vers l’infini négatif, tandis que la division arrondit le résultat à zéro.

Par exemple, -1 >> 1 est -1 , alors que -1/2 est 0 .

De la norme C:

Lorsque des entiers sont divisés, le résultat de l’opérateur / est le quotient algébrique avec toute partie fractionnaire rejetée.105) Si le quotient a / b est représentable, l’expression (a / b) * b + a% b doit être égale à a; sinon, le comportement de a / b et de% b est indéfini.

Il n’est pas difficile de penser à des exemples où les valeurs négatives pour a ne suivent pas cette règle avec un décalage arithmétique pur. Par exemple

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

qui satisfait à l’équation, alors que

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

n’est pas division avec troncature, et donc -2 * 4096 - 4095 n’est certainement pas égal à -8191.

Notez que le décalage des nombres négatifs est en réalité défini par l’implémentation, donc l’expression C (-8191) >> 12 n’a pas un résultat généralement correct selon la norme.