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 :

Nombres en virgule fixe


Sujet :

C

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 14
    Points : 10
    Points
    10
    Par défaut Nombres en virgule fixe
    Bonjour,
    je code pour une console portable n'ayant pas de processeur pour virgule flottante. Du coup dès que je commence à faire pas mal de calculs avec des float les perfomances chutent...
    N'ayant pas trouvé de librairie ou code gérant les nombres à virgule fixe simplement, je dois en coder une simplifiée. J'ai des problèmes pour le codage des multiplications: lorsque les 2 nombres sont positifs mon code semble bien marcher, mais quand il y a un nombre négatif les résultats sont faux.

    Extraits du code test pour mes fonctions:
    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
    36
    37
    38
    39
    40
    41
    42
    43
     
     
    #include <nds.h>
    #include <stdio.h>
     
    #define Fixed19_12tofloat(i) (((float)(i)) / (float)(1<<(12)))
    #define floattoFixed19_12(i) ((int)((i) * (float)(1<<(12))))
     
    typedef int fixed19_12;      // 1 signe, 19 partie entière, 12partie décimale
     
     
    // fonction posant problème
    // le calcul en morceaux est nécessaire pour ne pas tronquer la partie entière
    fixed19_12 multf19_12(fixed19_12 n1, fixed19_12 n2) {
        int i1,i2,f1,f2;
        f1=(n1&0x0FFF);                // récupération des parties décimales
        f2=(n2&0x0FFF);
        i1=((n1&0xFFFFF000)>>12); // récupération des parties entières
        i2=((n2&0xFFFFF000)>>12);
        return (((f1*f2)>>12) + (f1*i2) + (i1*f2) + ((i1*i2)<<12));
    }
     
     
     
    int main() {
        fixed19_12 a = floattoFixed19_12(0.625f);
        fixed19_12 b = floattoFixed19_12(-7.0625f);
     
        // calculs tests
     
        printf("a: %f   b: %f\n", Fixed19_12tofloat(a), Fixed19_12tofloat(b));
        printf("a+b: %f\n", Fixed19_12tofloat(a+b));
        printf("a-b: %f\n", Fixed19_12tofloat(a-b));
        printf("ax(-b): %f\n", Fixed19_12tofloat(multf19_12(a,-b)));
        printf("axb: %f\n", Fixed19_12tofloat(multf19_12(a,b)));
     
        /* Résultats:
        a: 0,625000  b: -7.062500
        a+b: -6.437500
        ax(-b): 4.414062
        axb: -393220.406250
        */
    }
    Voilà, la multiplication signée pose problème et je ne sais pas comment le résoudre. De plus ces multiplications devront intervenir dans des instants assez critiques de mon programme donc il faudrait qu'elles soient optimisées.

    Merci d'avance.

  2. #2
    Expert éminent sénior
    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
    Points : 13 926
    Points
    13 926
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        i1=((n1&0xFFFFF000)>>12); // récupération des parties entières
    0xFFFFF000 est un unsigned int. n1 sera converti automatiquement en unsigned int et le résultat du & sera un unsigned int. Le décalage va entrer des 0 en bits de poids forts et non pas étendre le bit de signe et le résultat est "faux" (si tes entiers signés sont en complément à 2)

    Pourquoi ne pas faire simplement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    fixed19_12 multf19_12(fixed19_12 n1, fixed19_12 n2) {
        return (n1*n2)>>12 ;
    }
    Attention, le résultat d'un décalage >> pour un nombre signé et négatif est défini par l'implémentation. Il faut vérifier que, pour ta console, ce décalage effectue bien une extension du signe . Sinon on peut écrire explicitement n1*n2/1000000000. A vérifier, les dépassements de capacités.
    Publication : Concepts en C

    Mon avatar : Glenn Gould

    --------------------------------------------------------------------------
    Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    Le problème si je fais une multiplication directement entre les 2 nombres, c'est que j'aurais une partie entière tronquée: si dans mon exemple je prend:
    a = b = 0x100
    Le résultat devrait tenir sans dépassement de capacité dans mon type. Or avec la multiplication directe, j'obtiens 0 comme résultat, à cause de la troncature des 12 bits les plus forts de ma partie entière.

    Par contre le problème venait bien de la conversion int / unsigned int, du coup j'ai refait mon code et ca semble marcher

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    fixed19_12 multf19_12(fixed19_12 n1, fixed19_12 n2) {
        int i1,i2,f1,f2;
        f1=(n1&0x0FFF);
        f2=(n2&0x0FFF);
        i1=((n1-f1)>>12);
        i2=((n2-f2)>>12);
        return (((f1*f2)>>12) + (f1*i2) + (i1*f2) + ((i1*i2)<<12));
    }
    Donc merci du conseil . Maintenant si quelqu'un sait s'il y a moyen de l'optimiser (avec de l'assembleur ?) ca serait bien.

  4. #4
    Expert éminent sénior
    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
    Points : 13 926
    Points
    13 926
    Par défaut
    C'est vrai qu'avec une multiplication directe, c'est plus rapide mais on a un dépassement de capacité si le résultat de la multiplication des nombres n'est pas compris entre -128.0 et ~128 (pour des int de 32 bits).

    Avec l'assembleur si la multiplication entière du processeur donne le résultat sur 64 bits, tu peux y trouver avantage en envisageant de coder directement (n1*n2) >>12 sans cette restriction.
    Publication : Concepts en C

    Mon avatar : Glenn Gould

    --------------------------------------------------------------------------
    Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    Il y a bien en effet une instruction assembleur sur le processeur pour avoir un résultat de multiplication sur 64bit en sauvant le résultat sur 2 registres. Mais j'ai jamais codé en assembleur alors je n'arrive pas à l'implémenter. Je pense avoir réussi le codage du calcul mais je ne sais pas où mettre la valeur de retour de la fonction (dans la pile, un registre spécifique, ou ailleurs ?).
    Je suppose que ca dépend du compilateur (gcc arm) et/ou du processeur (un ARM9). J'ai du mal a trouver de la doc la dessus.

    Code actuel:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    fixed19_12 asmmult(fixed19_12 n1, fixed19_12 n2) {
        asm (
        " LDMFD sp!,{r2,r3}      @ récupération de n1 et n2 dans la pile\n"
        " SMULL r0,r1,r2,r3      @ r0 = partie basse de r2*r3 ; r1 = partie haute\n"
        " MOV r0,r0,LSR #0x0C    @ r0 = r0>>8 \n"
        " ADD r0,r0,r1,LSL #0x14 @ r0 = r0 + r1<<20  Résultat de la fonction"
    );
    }

  6. #6
    Expert éminent sénior
    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
    Points : 13 926
    Points
    13 926
    Par défaut
    as-tu consulté ce document ?
    ARM GCC Inline Assembler Cookbook
    Publication : Concepts en C

    Mon avatar : Glenn Gould

    --------------------------------------------------------------------------
    Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    Je ne l'avais pas vu, j'ai pu remarqué que mon code était à moitié faux . Après 1h a me casser la tete j'ai toujours pas réussi à la faire marcher... Je ne comprends toujours pas ce qu'il faut faire de la valeur de retour.

  8. #8
    Expert éminent sénior
    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
    Points : 13 926
    Points
    13 926
    Par défaut
    Tu as des info plus complètes ici :
    6.39 Assembler Instructions with C Expression Operands
    et dans les paragraphes suivants.
    Publication : Concepts en C

    Mon avatar : Glenn Gould

    --------------------------------------------------------------------------
    Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !

  9. #9
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    Merci pour la doc ! Ca m'aura donné une belle migraine, mais c'est bon cette fois. La nouvelle fonction est 2x plus rapide que celle en C, c'est parfait.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    fixed19_12 asmmult(fixed19_12 n1, fixed19_12 n2) {
        asm (
        " STMFD sp!,{r2-r3}             @ sauvegarde de r2-r3 (sinon plantage)\n"
        " SMULL r2,r3,%[n1],%[n2]       @ r2 = partie basse de r0*r1 ; r3 = partie haute\n"
        " MOV r0,r2,ASR #12             @ r0 = r2>>12 \n"
        " ORR %[res],r0,r3,ASL #20      @ (res) = r0 + r3<<20  Résultat de la fonction\n"
        " LDMFD sp!,{r2-r3}"
        : [res] "=r" (n1)
        : [n1] "r" (n1), [n2] "r" (n2)
    );
        return n1;
    }
    Edit: version finale du code

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

Discussions similaires

  1. Nombre à virgule fixe
    Par dingoth dans le forum Général Java
    Réponses: 0
    Dernier message: 13/09/2010, 13h13
  2. représentation des nombres a virgules fixe en systemC
    Par MohEllayali dans le forum Bibliothèques
    Réponses: 0
    Dernier message: 10/03/2009, 22h50
  3. rendre un nombre de virgule flottante en virgule fixe
    Par mitnick2006 dans le forum Général Java
    Réponses: 3
    Dernier message: 15/08/2008, 21h44
  4. sum avec des nombres avec virgule
    Par Bruno2000 dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 30/09/2004, 15h01
  5. Chiffre a Virgule Fixe
    Par garybaldi dans le forum C
    Réponses: 3
    Dernier message: 21/06/2002, 10h41

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