web-dev-qa-db-fra.com

Comment soustraire deux entiers non signés avec bouclage ou débordement

Il y a deux entiers non signés (x et y) qui doivent être soustraits. x est toujours plus grand que y. Cependant, x et y peuvent s’envelopper; par exemple, s'il s'agissait de deux octets, 0x00 après 0xff. Le problème est que si x tourne autour, alors que y non. Maintenant, x semble être plus petit que y. Heureusement, x ne fera pas deux tours (une seule fois est garantie). En supposant que les octets, x a été encapsulé et est maintenant 0x2, alors que y ne l’a pas et est 0xFE. La bonne réponse de x - y est supposée être 0x4. 

Peut être, 

( x > y) ? (x-y) : (x+0xff-y);

Mais je pense qu'il y a une autre façon, quelque chose impliquant 2s compliment?, Et dans ce système embarqué, x et y sont les plus grands types int non signés, donc l'ajout de 0xff ... n'est pas possible

Quel est le meilleur moyen d'écrire la déclaration (la langue cible est le C)?

19
mgag

En supposant que deux unsigned integers:

  • Si vous savez que l'un est censé être "plus grand" que l'autre, il suffit de soustraire. Cela fonctionnera à condition que vous n'ayez pas travaillé plus d'une fois (évidemment, si vous l'avez fait, vous ne pourrez pas le savoir).
  • Si vous ne savez pas que l'un est plus grand que l'autre, soustrayez et convertissez le résultat en un entier signé de la même largeur. Cela fonctionnera à condition que la différence entre les deux se situe dans la plage de l'int signé (sinon, vous ne pourrez pas le savoir).

Pour clarifier: le scénario décrit par l'affiche originale semble semer la confusion, mais est typique des compteurs à largeur fixe qui augmentent de façon monotone, tels que les compteurs de ticks matériels ou les numéros de séquence dans les protocoles. Le compteur va (par exemple pour 8 bits) 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03 etc., et vous savez que des deux valeurs x et y que vous avez, x vient plus tard. Si x == 0x02 et y == 0xfe, le calcul x-y (résultat de 8 bits) donnera la réponse correcte de 4, en supposant que la soustraction de deux valeurs de n - bits enveloppe le modulo 2n - ce que C99 garantit pour la soustraction des valeurs non signées. (Remarque: le standard C ne pas garantit ce comportement pour la soustraction de signé valeurs.)

28
Matthew Slattery

Voici un peu plus de détails sur la raison pour laquelle cela "fonctionne" lorsque vous soustrayez le "plus petit" du "plus grand".

Quelques choses qui entrent dans cela…
1. Dans le matériel, la soustraction utilise l’addition: l’opérande approprié est simplement annulé avant d’être ajouté.
2. Dans le complément à deux (que presque tout utilise), un entier est annulé en inversant tous les bits, puis en ajoutant 1.

Le matériel le fait plus efficacement que ne le suggère la description ci-dessus, mais c’est l’algorithme de base pour la soustraction (même lorsque les valeurs ne sont pas signées).

Donc, passons à la figure 2 - 250 en utilisant des entiers non signés de 8 bits. En binaire nous avons

  0 0 0 0 0 0 1 0  
- 1 1 1 1 1 0 1 0

Nous nions l'opérande soustraite, puis ajoutons. Rappelons que pour nier nous inversons tous les bits puis ajoutons 1. Après avoir inversé les bits du deuxième opérande nous avons 

0 0 0 0 0 1 0 1  

Ensuite, après avoir ajouté 1, nous avons 

0 0 0 0 0 1 1 0  

Maintenant, nous effectuons l'addition ...

  0 0 0 0 0 0 1 0   
+ 0 0 0 0 0 1 1 0

= 0 0 0 0 1 0 0 0 = 8, which is the result we wanted from 2 - 250
20
Purdude

Peut-être que je ne comprends pas, mais qu'est-ce qui ne va pas avec:

unsigned r = x - y;

13
GManNickG

Comme indiqué, la question est déroutante. Vous avez dit que vous soustrayez des valeurs non signées. Si x est toujours plus grand que y, comme vous l'avez dit, alors x - y ne peut éventuellement pas encapsuler ou déborder. Donc, vous ne faites que x - y (si c'est ce dont vous avez besoin) et c'est tout.

3
AnT

C’est un moyen efficace de déterminer la quantité d’espace libre dans un tampon circulaire ou de contrôler le flux de la fenêtre glissante. Utilisez unsigned ints pour head et tail - incrémentez-les et laissez-les revenir à la fin! être une puissance de 2.

free = ((head - tail) & size_mask), où size_mask est égal à 2 ^ n-1 la taille de la mémoire tampon ou de la fenêtre.

2
C guy

Le problème devrait être énoncé comme suit:

Supposons que la position (angle) de deux pointeurs a et b d'une horloge est donnée par un uint8_t. La circonférence entière est divisée en 256 valeurs d'un uint8_t. Comment calculer efficacement la plus petite distance entre les deux pointeurs?

Une solution est:

uint8_t smaller_distance = abs( (int8_t)( a - b ) );

Je pense que rien n’est plus efficace, sinon il y aurait quelque chose de plus efficace que abs ().

1
hpc64

Juste pour mettre la réponse déjà correcte dans le code:

Si vous savez que x est la plus petite valeur, le calcul suivant fonctionne:

int main()
{
    uint8_t x = 0xff;
    uint8_t y = x + 20;
    uint8_t res = y - x;
    printf("Expect 20: %d\n", res); // res is 20

    return 0;
}

Si vous ne savez pas lequel est le plus petit:

int main()
{
    uint8_t x = 0xff;
    uint8_t y = x + 20;
    int8_t res1 = (int8_t)x - y;
    int8_t res2 = (int8_t)y - x;
    printf("Expect -20 and 20: %d and %d\n", res1, res2);

    return 0;
}

Où la différence doit se situer dans la plage de uint8_t dans ce cas.

L'expérience de code m'a aidé à mieux comprendre la solution.

0
Tarion

Pour faire écho à tous les autres qui répondent, si vous vous contentez de soustraire les deux et d'interpréter le résultat comme non signé, tout ira bien. 

Sauf si vous avez un contre-exemple explicite.

Votre exemple de x = 0x2, y= 0x14 n'entraînera pas 0x4, mais plutôt 0xEE, sauf si vous avez davantage de contraintes sur le calcul qui ne sont pas précisées.

0
MSN