C ++: différence de deux entiers 64 bits non signés dans un entier 64 bits signé
-
29-10-2019 - |
Question
J'essaie d'écrire une fonction en C ++ qui prend deux entiers non signés 64 bits et renvoie leur différence dans un entier 64 bits signé. Cela semble un peu compliqué en raison de la situation de débordement - car les entrées sont deux entiers positifs non signés, si la différence absolue entre ces deux est supérieure à la valeur signée maximale (int64_max), alors la différence ne peut pas être transmise via un entier signé. J'ai donc écrit l'implémentation suivante, et je me demandais, d'abord, si c'est correct fonctionnellement, et deuxièmement, y a-t-il une implémentation plus simple. Toutes les suggestions seraient grandement appréciées. Merci! (Je vais remplacer l'affirmation par une exception, elle est juste là pour le moment!)
int64_t GetDifference(uint64_t first, uint64_t second) {
uint64_t abs_diff = (first > second) ? (first - second): (second - first);
uint64_t msb_abs_diff = (abs_diff >> (sizeof(abs_diff)*8 - 1)) & 1;
assert(msb_abs_diff == 0);
int64_t diff = first - second;
return diff;
}
La solution
Pour moi, cela semble une implémentation plus simple et plus lisible.
int64_t GetDifference(uint64_t first, uint64_t second) {
uint64_t abs_diff = (first > second) ? (first - second): (second - first);
assert(abs_diff<=INT64_MAX);
return (first > second) ? (int64_t)abs_diff : -(int64_t)abs_diff;
}
Autres conseils
Trois nitpicks:
sizeof(abs_diff)*8 - 1
peut être remplacé par le littéral63
sans perte de portabilité (en fait, ce sera Suite portable en raison de plates-formes où unchar
n'est pas de 8 bits de large)& 1
n'est pas nécessaire, car le résultat du changement est toujours un bit- Vous pouvez dériver
diff
deabs_diff
sans répéter la soustraction.
Sinon, cela me semble parfaitement correct.
C'est plus court et probablement plus rapide.
int64_t GetDifference(uint64_t first, uint64_t second)
{
int64_t diff = first - second;
bool overflowed = (diff < 0) ^ (first < second);
assert(!overflowed);
return diff;
}
Un bon compilateur d'optimisation devrait remarquer que diff < 0
est le drapeau négatif et first < second
est le drapeau de transport de l'expression antérieure. La comparaison de ces deux drapeaux est le test classique pour le débordement.
Même s'il ne détecte pas cela, il y a moins d'opérations requises.
Mais la plus grande raison pour laquelle je préfère cela Il n'y a pas de chiffres magiques.
Que dis-tu de ça:
int64_t GetDifference(uint64_t first, uint64_t second) {
int64_t diff = (int64_t)(first - second);
assert first >= second && diff >= 0 || first < second && diff < 0;
return diff;
}