32 votes

Déclaration de variables 64 bits en C

J'ai une question.

 uint64_t var = 1; // this is 000000...00001 right?
 

Et dans mon code, cela fonctionne:

 var ^ (1 << 43)
 

Mais comment sait-il que 1 doit être en 64 bits? Ne devrais-je pas écrire ceci à la place?

 var ^ ( (uint64_t) 1 << 43 )
 

34voto

Matteo Italia Points 53117

Comme vous l'avez supposé, 1 est une plaine signée int (ce qui, probablement, sur votre plate-forme est de 32 bits de large, en complément de 2 arithmétique), et 43, de sorte que, par hasard, 1<<43 résultats dans un débordement: dans les faits, si les deux arguments sont de type int de l'opérateur règles exigent que le résultat sera un int ainsi.

Encore, dans C signé débordement d'entier est un comportement indéfini, donc dans la ligne du principe que tout peut arriver. Dans votre cas, probablement le compilateur de code émis pour effectuer ce changement d'une version 64 bits de registre, de sorte que , par chance, il semble fonctionner; pour obtenir une garantie de résultat correct, vous devez utiliser la deuxième forme que vous avez écrit, ou, en alternative, spécifiez 1 comme unsigned long long littérale à l'aide de l' ull suffixe (unsigned long long est assuré d'être au moins 64 bits).

var ^ ( 1ULL << 43 )

11voto

chux Points 13185

Je recommande OP approche.

Pour l'OP du petit exemple, le 2-dessous sera probablement effectuer la même.

uint64_t var = 1; 
// OP solution)
var ^ ( (uint64_t) 1 << 43 )
// Others suggested answer
var ^ ( 1ULL << 43 )        

Les résultats ci-dessus ont la même valeur, mais de types différents. La différence de potentiel réside dans la façon de 2 types existent dans C: uint64_t et unsigned long long , et ce qui pourrait suivre.

uint64_t a une portée exacte de 0 à 264-1.
unsigned long long a une plage de 0 à au moins 264-1.

Si unsigned long long sera toujours : 64-bits, comme il semble être sur une machine il ya beaucoup de jours, il n'y a pas de problème, mais regardons vers l'avenir et dire que ce code a été exécuté sur une machine où unsigned long long était de 16 octets (0 à au moins 2128-1).

Un exemple artificiel ci-dessous: Le premier résultat de l' ^ est uint64_t, lorsqu'il est multiplié par 3, le produit sera encore uint64_t, la réalisation d'un modulo 264, devrait dépassement de se produire, alors le résultat est affecté d1. Dans le cas suivant, le résultat d' ^ est unsigned long long et lorsqu'il est multiplié par 3, le produit peut être plus grand que 264 qui est alors attribué d2. Donc, d1 et d2 ont une réponse différente.

double d1, d2;
d1 = 3*(var ^ ( (uint64_t) 1 << 43 ));
d2 = 3*(var ^ ( 1ULL << 43 ));

Si l'on veut travailler avec des unit64_t, être cohérent. Ne pas assumer unit64_t et unsigned long long sont les mêmes. Si c'est OK pour votre réponse à un unsigned long long, amende. Mais dans mon expérience, si l'on commence à l'aide fixe de taille moyenne de type uint64_t, on ne veut pas que la variante de la taille des types de gâcher les calculs.

4voto

Nikolai N Fetissov Points 52093

var ^ ( 1ULL << 43 ) devrait le faire.

4voto

Alok-- Points 506

Un portable pour avoir un unit64_t constante est d'utiliser UINT64_C macro (à partir de stdint.h):

UINT64_C(1) << 43

Les plus susceptibles UINT64_C(c) est défini à quelque chose comme c ## ULL.

Du C standard:

La macro INTN_C(value) s'étendre à un nombre entier d'expression constante correspondant au type int_leastN_t. La macro UINTN_C(value)sont l'étendre à un nombre entier d'expression constante correspondant au type uint_leastN_t. Par exemple, si uint_least64_t est un nom pour le type unsigned long long int, alors UINT64_C(0x123) peuvent se développer à l' constante entière 0x123ULL.

3voto

Gilles Points 37537

Votre compilateur ne sait pas que le changement doit être fait en 64 bits. Cependant, avec cette version du compilateur dans cette configuration particulière de ce code, deux torts arriver à faire un droit. Ne comptez pas sur elle.

En supposant que l' int est un 32-bits type de votre plate-forme (ce qui est très probable), les deux torts 1 << 43 sont:

  • Si le décalage est supérieur ou égal à la largeur du type de l'opérande de gauche, le comportement est indéfini. Cela signifie que si x est de type int ou unsigned int, alors x << 43 a un comportement indéfini, comme n' x << 32 ou de toute autre x << nn ≥ 32. Par exemple 1u << 43 aurait un comportement indéfini trop.
  • Si l'opérande de gauche est un type signé, et le résultat de l'opération des débordements de ce type, alors le comportement est indéfini. Par exemple 0x12345 << 16 a un comportement indéfini, parce que le type de l'opérande de gauche est le type signé int , mais la valeur de résultat ne rentre pas dans int. D'autre part, 0x12345u << 16 est bien définie et la valeur 0x23450000u.

"Un comportement indéfini" signifie que le compilateur est libre de générer du code qui se bloque ou renvoie un résultat erroné. Il se trouve que vous avez obtenu le résultat souhaité dans ce cas ce n'est pas interdit, cependant, la loi de Murphy dicte qu'un jour, le code généré ne pas faire ce que vous voulez.

Afin de garantir que l'opération se déroule sur une version 64 bits de type, vous devez vous assurer que l'opérande de gauche est un 64-bits type - le type de la variable que vous assignez le résultat n'a pas d'importance. C'est la même question que float x = 1 / 2 résultant en x contenant 0 et non de 0,5: uniquement les types des opérandes de déterminer le comportement de l'opérateur arithmétique. Tout d' (uint64)1 << 43 ou (long long)1 << 43 ou (unsigned long long)1 << 43 ou 1ll << 43 ou 1ull << 43 le fera. Si vous utilisez un type signé, alors le comportement est défini que si il n'y a pas de dépassement de capacité, de sorte que si vous attendez de la troncature sur le dépassement, assurez-vous d'utiliser un type non signé. Un type non signé est généralement recommandé, même si le dépassement n'est pas censé se produire parce que le comportement est reproductible - si vous utilisez un type signé, le simple acte de l'impression des valeurs à des fins de débogage pourrait modifier le comportement (parce que les compilateurs profiter de comportement indéfini pour générer n'importe quel code est le plus efficace, sur un micro-niveau, qui peuvent être très sensibles à des choses comme la pression sur l'allocation de registres).

Depuis que vous avez l'intention de la raison d'être de type uint64_t, c'est plus clair pour effectuer tous les calculs avec ce type. Donc:

uint64_t var = 1;
… var ^ ((uint64_t)1 << 43) …

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X