Efficace non signé-à-signé fonte d'éviter la mise en œuvre définies par le comportement

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 bizarre 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, 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 casting de int la mise en œuvre est définie pour certains intrants, les acteurs de 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 de" ressembler?

Pour résumer, je veux:

  • Fonte de unsigned int signé int
  • De préserver la valeur mod UINT_MAX+1
  • Invoquer standard mandat comportement
  • Compiler dans un no-op sur un deux-complément de la machine avec le compilateur optimisant

[Mise à jour]

Permettez-moi de donner un exemple pour montrer pourquoi ce n'est pas une question triviale.

Envisager un hypothétique implémentation C++ 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 égale à 232 - 1
  • Arithmétique sur int modulo 232 (dans la gamme INT_MIN par INT_MAX)
  • std::numeric_limits<int>::is_modulo est vrai
  • Casting unsigned n int préserve la valeur de 0 <= n <= 32767 et les rendements zéro sinon

Sur cette hypothétique mise en œuvre, il y a exactement un int valeur congruents (mod UINT_MAX+1) pour chaque unsigned 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 que std::numeric_limits<int>::is_modulo est totalement 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 mal.

[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 même 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 de/ceux-complément/signe-amplitude.

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é 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.

  • En fait, je l'ai dit exactement ce que je veux dans ce cas: "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." C'est, je veux le "droit" signé int à condition qu'il existe. Si elle n'existe pas-comme cela pourrait se produire dans le cas, par exemple, de rembourrage bits ou en complément des représentations -- je veux détecter et de les traiter pour l'invocation de la fonte.
  • désolé, pas sûr de savoir comment j'ai manqué.
  • Btw, je pense que dans votre hypothétique délicate de la mise en œuvre int a besoin d'au moins 33 bits pour le représenter. Je sais que c'est seulement une note de bas de page, de sorte que vous pouvez faire valoir qu'il n'est pas normatif, mais je pense que la note de bas de page 49 en C++11 est prévu pour être vrai (puisque c'est la définition d'un terme utilisé dans le standard) et il n'est pas en contradiction avec quelque chose explicitement mentionné dans le texte normatif. Donc, toutes les valeurs négatives doivent être représentée par une séquence de bits dont le bit le plus élevé est défini, et par conséquent, vous ne pouvez pas cram 2^32 - 32768 en 32 bits. Non pas que votre argument repose en aucune manière sur la taille de int.
  • Et concernant vos modifications dans hvd réponse, je pense que vous avez mal interprété la note 49. Vous dites que signe-amplitude est interdit, mais il ne l'est pas. Vous avez lu comme: "les valeurs représentées par les bits suivants sont additifs, commencer par 1, et (sont multipliés par les intégrales puissance de 2, sauf peut-être pour le bit à la position la plus haute)". Je crois qu'il faut le lire, "les valeurs représentées par une succession de bits (sont additifs, commencer par 1, et sont multipliés par les intégrales de puissance de 2), sauf peut-être pour le bit à la position la plus élevée". C'est, tous les paris sont éteints si le bit est défini.
  • Votre interprétation est peut-être exacte. Si oui, il n'règle mon hypothétique... Mais il introduit également un très vaste nombre de possibilités, faisant de cette question extrêmement difficile de répondre. En fait, cela ressemble à un bug dans la spec pour moi. (Apparemment, le C comité pensais et il fixe seront raffinés en C99. Je me demande pourquoi C++11 ne pas adopter leur approche?)
  • Si une application utilise un " complément ou le signe-amplitude format, sont des opérateurs booléens définie comme l'exploitation des matières bits, ou sont-ils défini comme un fonctionnement dans un mode compatible avec le complément à deux de format (dans ce cas, la seule signification de la représentation devrait être (1), le comportement de ~INT_MAX, et (2), les syndicats ou d'autres techniques d'aliasing types.
  • Je pense que tu veux dire "les opérateurs au niveau du bit", pas "opérateurs Booléens". (Le dernier fonctionner sur les Booléens; par exemple, && ou ||.) Je crois que la réponse à votre question est que les opérateurs au niveau du bit (comme & et |) fonctionnent sur la "valeur de la représentation de l'entier. Si vous souhaitez assurer un comportement cohérents avec en complément à deux sur le format" vous avez besoin de jeter les arguments de leurs les formulaires non signés.
  • En d'autres termes, étant donné int n;, réellement portable calcul de "n mod 16" [par opposition à "n reste 16"] nécessite (unsigned)n & 15 plutôt que de simplement n & 15? Je me demande combien de code utilise délibérément des opérateurs au niveau du bit sur autre chose que complément à deux valeurs? Je souhaite que le C comité des normes permettrait d'ajouter les manières de préciser qu'un programme doit utiliser notamment entier de la sémantique ou de refuser de compilation; comme il est, de la langue, il est beaucoup plus facile d'écrire du code qui semble correct, mais s'arrête sur quelques raisonnable implémentations que d'écrire du code portable.

InformationsquelleAutor Nemo | 2012-10-31