IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Saisie de nombre en complément à 2


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut Saisie de nombre en complément à 2
    Bonjour,

    Tout est presque dans le titre. Il y a quelques temps, j'avais codé pour le boulot une petite application pour entrer des nombres en console et les convertir en décimal ou en hexadécimal selon le mode d'entrée. Pas de soucis tant qu'on reste sur des nombres positifs et que 0xF est considéré comme valant 15 et non -1.

    Ça s'est très sérieusement corsé quand j'ai voulu passer à des nombres négatifs en compléments à 2. Je voulais que, dans ce cas, 0xF soit interprété comme -1. Pas de soucis pour saisir un nombre négatif et le convertir en hexadécimal si on n'est pas trop exigeant sur le nombre de F rajoutés devant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    printf("-1 = %X\n\n", -1);
    // Sortie : 
    // -1 = FFFFFFFF
    La technique que j'ai adopté pour les nombres saisis en hexadécimal est la suivante :
    1. saisie d'une chaine de caractères
    2. conversion en nombre
    3. si le nombre doit être interprété comme négatif, alors faire un masque pour "étendre le MSB" et avoir tout plein de F ; s'il est positif, alors aucun traitement à cette étape
    4. prendre le complément à 2
    5. afficher le résultat


    Globalement, ça marche bien. 0xF est interprété comme -1 ; 0x0F comme 15.

    J'ai deux soucis :
    1. le traitement aux limites, pour détecter les dépassements de format
    2. la portabilité du masque car il est fait de façon bourrin :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
                  // Traitement particulier pour les nombres negatifs
                  if( !(str[0]>='0' && str[0]<'8') )
                  {
                      // Extend MSB
                      switch(strlen(str))
                      {
                          case(1):
                              number = 0xFFFFFFF0UL | number;
                              break;
                          case(2):
                              number = 0xFFFFFF00UL | number;
                              break;
                          case(3):
                              number = 0xFFFFF000UL | number;
                              break;
                          case(4):
                              number = 0xFFFF0000UL | number;
                              break;
                          case(5):
                              number = 0xFFF00000UL | number;
                              break;
                          case(6):
                              number = 0xFF000000UL | number;
                              break;
                          case(7):
                              number = 0xF0000000UL | number;
                              break;
                          case(8):
                              number = 0x00000000UL | number;
                              break;
                          default:
                              printf("WTF?\n");
                              number = 0;
                              break;
                      }

    Je travaille sous XP en 32 bits, donc un long fait 4 octets. Quand je vais passer sur 64 bits...

    Je ne suis pas certain que ma méthode soit la bonne.... Et je viens donc demander vos avis ! Je vous joins un fichier compilable pour les plus motivés d'entre vous : il donne une application console où vous n'avez qu'à taper des nombre en hexa et faire ENTREE. La boucle se termine quand vous entrer 0.

    Merci d'avance pour vos remarques

    Bktero.
    Fichiers attachés Fichiers attachés

  2. #2
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    A la place du switch :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    number |= (unsigned long)(-1)<< 4*strlen(str) ;

  3. #3
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Ca m'a l'air de toute beauté comme solution !



    Dans ma recherche des cas aux limites, je n'arrive pas à comprendre le résultat du programme suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    int main(void)
    {
        printf("-1 = %X\n", -1);
     
        puts("");
        printf("LONG_MAX = %X\n", LONG_MAX);
        printf( "Strtol  = %ld\n", strtol("0x7fffffff", NULL, 16) );
        printf( "Strtoul = %ld\n", strtoul("0x7fffffff", NULL, 16) );
     
     
        puts("");
        printf("LONG_MIN = %X\n", LONG_MIN);
        printf( "Strtol  = %ld\n", strtol("0x80000000", NULL, 16) );
        printf( "Strtoul = %ld\n", strtoul("0x80000000", NULL, 16) );
     
        puts("");
        char nbrstr[256];
        sprintf(nbrstr, "%X", LONG_MIN );
        printf("Chaine obtenue : %s\n", nbrstr);
        printf( "Strtol  = %ld\n", strtol( nbrstr , NULL, 16) );
        printf( "Strtoul = %ld\n", strtoul( nbrstr , NULL, 16) );
    -1 = FFFFFFFF

    LONG_MAX = 7FFFFFFF
    Strtol = 2147483647
    Strtoul = 2147483647

    LONG_MIN = 80000000
    Strtol = 2147483647
    Strtoul = -2147483648

    Chaine obtenue : 80000000
    Strtol = 2147483647
    Strtoul = -2147483648


    .......Fin
    strtol n'est-elle pas censé pouvoir convertir un nombre entre LONG_MAX et LONG_MIN inclus ?

  4. #4
    Membre Expert
    Profil pro
    Développeur en systèmes embarqués retraité
    Inscrit en
    Mars 2006
    Messages
    952
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2006
    Messages : 952
    Par défaut
    Salut,

    En fait je pense que ton
    LONG_MIN = 80000000
    doit être interprété comme
    LONG_MIN = -0x80000000
    car un entier 32 signé ne peut pas valoir 0x80000000

    Le vrai ULONG_MAX est défini dans <limits.h>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    /*
     * Maximum and minimum values for longs and unsigned longs.
     *
     * TODO: This is not correct for Alphas, which have 64 bit longs.
     */
    #define LONG_MAX	2147483647L
    #define LONG_MIN	(-LONG_MAX-1)
     
    #define ULONG_MAX	0xffffffffUL
    On voit qu'il doit être suffixé par UL. Je ne comprends pas vraiment qu'il n'y ait pas de ligne
    Mais bon, il y tellement de choses que je ne comprends pas en C...

    A+,

    Pfeuh

  5. #5
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par diogene Voir le message
    A la place du switch :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    number |= (unsigned long)(-1)<< strlen(str) ;
    Après test, je crois que ça doit en fait être :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    number |= (unsigned long)(-1) << 4 * strlen(str) ;


    LONG_MIN = 80000000
    doit être interprété comme
    LONG_MIN = -0x80000000
    car un entier 32 signé ne peut pas valoir 0x80000000
    Le vilain !
    Je trouve ça quand même bizarre, puisque :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        signed long mini = LONG_MIN;
        printf("mini = %ld", mini);
    affiche bien :
    mini = -2147483648
    LONG_MIN vaut bien 0x80000000, donc un entier sur 32 bits (un long) peut valoir 8000 0000, je ne me trompe pas ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    printf("(LONG_MAX)*(-1)-1 = %lX\n", (LONG_MAX)*(-1)-1 ) ;
    affiche :
    (LONG_MAX)*(-1)-1 = 80000000

    Si je m'emmêle pas trop les pinceaux, je dois bien travailler avec un long et non un unsigned long. En effet, je veux un nombre signé sur 32 bits et mon PC les code bien en complément à 2 ?

  6. #6
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    - Pour afficher les unsigned long, le format devrait être %lu

    - strtol() attend non pas un format dérivé du complément à deux écrit en hexa, mais un format avec signe + valeur absolue, donc avec un + (par défaut) ou un -suivi de la valeur en hexa.
    Alors, 0x80000000 dépasse la capacité (en positif) et strtol() renvoie LONG_MAX
    Par contre -0x80000000 donnera LONG_MIN


    Après test, je crois que ça doit en fait être :
    ...
    Oui, tes chiffres sont en hexa, erreur d'inattention de ma part. Je corrige mon post

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par diogene Voir le message
    - Pour afficher les unsigned long, le format devrait être %lu
    Si tu fais référence à ça printf( "Strtoul = %ld\n", strtoul("0x7fffffff", NULL, 16) );, c'était une faute volontaire pour voir la différence uniquement la différence de strtoul.

    Citation Envoyé par diogene Voir le message
    Oui, tes chiffres sont en hexa, erreur d'inattention de ma part. Je corrige mon post
    Pas de soucis J'avais fait un test avec 0xA dans un programme séparé ça marchait bien ; je me suis pris la tête à comprendre pour dans mon programme principal, A n'était pas correctement converti.

    Citation Envoyé par diogene Voir le message
    - strtol() attend non pas un format dérivé du complément à deux écrit en hexa, mais un format avec signe + valeur absolue, donc avec un + (par défaut) ou un -suivi de la valeur en hexa.
    Alors, 0x80000000 dépasse la capacité (en positif) et strtol() renvoie LONG_MAX
    Par contre -0x80000000 donnera LONG_MIN
    Aaaahhhhhh !!!!!!!!!!!!!! Je comprends beaucoup mieux maintenant.

    Ca remet largement en cause ce que j'ai fait jusqu'à maintenant dans le choix des types de données utilisés. Il va falloir repenser la conversion des grands nombres négatifs...

  8. #8
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Hop hop ! J'ai continué à travailler et j'ai progressé puisque la chaine 80000000 a bien été convertie en -2147483648 ^^


    Je rajoute une petite modification au masque donné par Diogene. Il doit être de type unsigned long long et non de type unsigned long. En effet, un 'unsigned long long' pourra subir un decalage de 8*sizeof(unsigned long) bits (ce qui correspond bien à à 4*strlen d'une chaine de 8 caractères hexadécimaux) et le résultat sera garanti par la norme. Je vous cite le point 6.5.7.3 de la norme :

    "The integer promotions are performed on each of the operands.
    The type of the result is that of the promoted left operand.
    If the value of the right operand is negative or is
    greater than or equal to the width of the promoted left operand,
    the behavior is undefined."
    Sous XP / Codeblocks, on obtient FFFFFFFF et non 00000000 pour le decalage maximum.



    Pour me jouer de strtol avec les chaines sur 8 lettres représentants un nombre, j'ai rusé en vérifiant que c'était convertible (ie : que la chaine saisie par l'utilisateur est une chaine hexa) avec strtoul.


    J'y retourne

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Débutant] Saisie de nombre par l'utilisateur en matlab
    Par bsangoku dans le forum MATLAB
    Réponses: 4
    Dernier message: 22/04/2010, 07h39
  2. Limiter la saisie à un nombre de caractère
    Par 0nn2belle dans le forum C
    Réponses: 8
    Dernier message: 04/12/2008, 17h09
  3. masque saisie format nombre
    Par ninette24 dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 15/07/2008, 13h29
  4. Empêcher la saisie de nombre ou de caractère
    Par SOPSOU dans le forum Langage
    Réponses: 3
    Dernier message: 28/08/2007, 17h27
  5. [JFormattedTextField] Saisie des nombres
    Par n00bi dans le forum Composants
    Réponses: 2
    Dernier message: 20/06/2005, 14h23

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo