Je veux définir une fonction qui prend un unsigned int
comme argument et renvoie un int
congrus modulo UINT_MAX+1 pour l'argument.
Une première tentative pourrait ressembler à ceci:
int unsigned_to_signed(unsigned n)
{
return static_cast<int>(n);
}
Mais comme toute langue, l'avocat sait, la conversion de non signé signé pour des valeurs plus grandes que INT_MAX la mise en œuvre est définie.
Je veux mettre en œuvre la présente tel que (a) il ne repose que sur le comportement mandaté par la spécification; et (b) il compile dans un no-op sur toute machine moderne et d'optimisation du compilateur.
Comme pour les étranges machines... Si il n'y a pas signé d'int congrus modulo UINT_MAX+1 pour les unsigned int, disons que je veux lancer une exception. Si il n'y a plus d'un (je ne suis pas sûr que c'est possible), disons que je veux la plus grande.
OK, la deuxième tentative:
int unsigned_to_signed(unsigned n)
{
int int_n = static_cast<int>(n);
if (n == static_cast<unsigned>(int_n))
return int_n;
// else do something long and complicated
}
Je n'ai pas beaucoup de soins au sujet de l'efficacité quand je ne suis pas sur un deux-système du complément, car à mon humble avis, c'est peu probable. Et si mon code devient un goulet d'étranglement sur l'omniprésence d'un signe-amplitude systèmes de 2050, eh bien, je parie que quelqu'un peut comprendre et d'optimiser l'époque.
Maintenant, cette deuxième tentative est assez proche de ce que je veux. Bien que le cast int
est mise en œuvre définies pour certains intrants, la fonte de retour à l' unsigned
est garanti par la norme afin de préserver la valeur modulo UINT_MAX+1. Si la condition n'vérifier exactement ce que je veux, et compiler en rien sur tout le système je suis susceptible de rencontrer.
Cependant... je suis encore un casting pour int
sans vérifier d'abord si elle va invoquer la mise en œuvre définies par le comportement. Sur un hypothétique système en 2050, elle pourrait faire de qui-sait-quoi. Donc, disons que je veux éviter.
Question: Quelle devrait être ma "troisième tentative"?
Pour résumer, je veux:
- Moulés à partir de unsigned int signé int
- Préserver la valeur mod UINT_MAX+1
- Invoquer standard mandat comportement
- Compiler dans un no-op sur un deux-en complément de la machine avec l'optimisation du compilateur
[Mise à jour]
Permettez-moi de donner un exemple pour montrer pourquoi ce n'est pas une question triviale.
Prenons le C++ mise en œuvre avec les propriétés suivantes:
-
sizeof(int)
est égal à 4 -
sizeof(unsigned)
est égal à 4 -
INT_MAX
est égal à 32767 -
INT_MIN
est égal à -232 + 32768 -
UINT_MAX
est égal à 232 - 1 - L'arithmétique sur
int
modulo 232 (dans la gammeINT_MIN
parINT_MAX
) -
std::numeric_limits<int>::is_modulo
est vrai - Casting unsigned
n
d'int conserve la valeur pour 0 <= n <= 32767 et les rendements zéro sinon
Sur cette hypothétique mise en œuvre, il y a exactement un int
de la valeur congruents (mod UINT_MAX+1) pour chaque unsigned
de la valeur. Donc ma question serait bien définis.
Je prétends que cet hypothétique C++ mise en œuvre pleinement conforme à la C++98, C++03, et de C++11 cahier des charges. J'avoue que je n'ai pas mémorisé chaque parole de tous... Mais je crois que j'ai lu les sections pertinentes soigneusement. Donc, si vous voulez que j'accepte votre réponse, vous devez (a) citer un spec que les règles de cette hypothétique mise en œuvre ou (b) les manipuler correctement.
En effet, une réponse correcte doit gérer chaque hypothétique mise en œuvre permise par la norme. C'est ce qui "invoquer standard mandat comportement" signifie, par définition.
D'ailleurs, notez qu' std::numeric_limits<int>::is_modulo
est tout à fait inutile ici pour de multiples raisons. Pour une chose, il peut être true
même si non signé-à-signé jette de ne pas travailler pour de grandes valeurs non signées. Pour l'autre, il peut être true
même sur un complément ou un signe-amplitude systèmes, si l'arithmétique est simplement modulo l'ensemble de l'intervalle entier. Et ainsi de suite. Si votre réponse dépend is_modulo
, c'est faux.
[Mise à jour 2]
hvd réponse m'a appris quelque chose: Mon hypothétique implémentation C++ pour les entiers est pas permise par les techniques modernes de C. C99 et C11 normes sont très spécifiques au sujet de la représentation des entiers signés; en effet, ils ne permettent que deux en complément, ceux-compléter et signer magnitude (section 6.2.6.2 paragraphe (2); ).
Mais le C++ n'est pas C. Comme il s'avère, de ce fait se trouve au cœur de ma question.
Le C++98 norme a été basé sur la plus ancienne C89, qui dit (section 3.1.2.5):
Pour chaque entier signé types, il existe un correspondant (mais différents) type entier non signé (désigné par le mot-clé non signé), qui utilise la même quantité de stockage (y compris le signe de l'information) et a les mêmes exigences alignement. La gamme de non négatif valeurs d'un entier signé de type est un sous-groupe de la correspondant de type entier non signé, et la représentation de la même valeur dans chaque type est le même.
C89 ne dit rien sur un seul bit de signe ou permettant seulement deux-complément/-complément d'ouverture de l'ampleur.
Le C++98 standard adopté cette langue presque mot à mot (section 3.9.1 paragraphe (3)):
Pour chaque entier signé types, il existe un correspondant (mais différentes) type entier non signé: "
unsigned char
", "unsigned short int
", "unsigned int
"et "unsigned long int
", chacun de qui occupe la même quantité de stockage et a le même alignement exigences (3.9) que le type entier signé ; que est, chaque entier signé de type a, l'objet même de la représentation comme son correspondant entier non signé de type. La gamme de positif les valeurs d'un entier signé de type est un sous-groupe de correspondants type entier non signé, et la valeur de la représentation de chaque correspondant signed/unsigned type doit être le même.
Le C++03 standard utilise essentiellement identique de la langue, comme le fait de C++11.
Pas de C++ standard spec limite son entier signé auprès de tout C spec, autant que je puis dire. Et il n'y a rien de mandater un seul bit de signe ou quelque chose du genre. Tout ce qu'elle dit, c'est que non-négatif entiers signés doivent être un sous-groupe de correspondants non signé.
Donc, encore une fois je demande que INT_MAX=32767 avec INT_MIN=-232+32768 est autorisée. Si votre réponse suppose sinon, elle est incorrecte, à moins que vous citez un C++ standard prouver que j'ai tort.