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 :

rounding hyper rapide en c/c++


Sujet :

C

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Août 2006
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 45
    Par défaut rounding hyper rapide en c/c++
    Bonjour,

    Question a priori simple si il en est, mais quand même:

    quelle est la routine la plus rapide en C/C++ pour calculer un arrondi à n décimales?

    pour le moment j'utilise ça:
    inline double round(double dNum, double dStep)
    { // rounds to closest dStep increment
    double dVal = dStep * ((dStep > 0) ? (int)(dNum / dStep) : 0);
    if (dNum-dVal<dStep/2) return dVal;
    else return dVal + dStep;
    }

    il doit exister mieux.

    d'avance merci,

    bv

  2. #2
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    est-ce que :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    return ( dStep * floor(dNum/dStep) ) ;
    irait plus vite ?

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Août 2006
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 45
    Par défaut
    Merci, mais sauf erreur de ma part:

    floor(dNum/dStep) ~ (int)(dNum/dStep)

    donc c'est un arrondi à l'incrément inférieur, n'est ce pas?
    Je cherche l'arrondi au plus proche et vraiment le plus rapide.


    bv

  4. #4
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    oui mais si tu fais floor((dNum+0.5)/dStep) ça le fait pas ??

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Août 2006
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 45
    Par défaut
    le 0.5 serait un dStep/2 pour moi, mais ça peut le faire.
    'floor' est plus rapide qu'un cast en int ?

    merci
    bv

  6. #6
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    aucune idée....

  7. #7
    Inactif  

    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    534
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 534
    Par défaut
    Salut,

    Je tenterais bien

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
     
    double  round_double(  double d , unsigned long precision )
    { double r = (floor( d* precision + 0.5))  ;
      return r ;
    }
    précision 7 chiffres = 10000000L ;

  8. #8
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    ça ne fait pas ce qu'il veut....

  9. #9
    Inactif  

    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    534
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 534
    Par défaut
    Salut,

    Ben oui je me suis trompé...

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Août 2006
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 45
    Par défaut
    J'ai testé jusqu'à plus de 10^10 itérations et aucune différence entre floor et un cast en int. 'floor' me paraît toutefois plus "propre".

    J'en reste donc à:

    inline double round(double dNum, double dStep)
    { // rounds to closest dStep increment
    double dVal = dStep * ((dStep > 0) ? floor(dNum / dStep) : 0);
    return (dNum-dVal<dStep/2) ? dVal : dVal + dStep;
    }

    au moins pour éviter la div/0

    Merci,

    bv

  11. #11
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par me_myself
    au moins pour éviter la div/0

    Merci,

    bv

    euhhhh ????

    si tu veux du sûr et rapide , il me semble que :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    inline double round(double dNum, double dStep)
    {
    if ( dStep < DBL_EPSILON )
         return 0.0 ;  // ou return Nan
    else
         return (dStep*floor((dNum+0.5)/dStep));
    }
    est beaucoup plus rapide...

    Au pire :

    1 test +
    1 addition +
    1 division +
    1 multiplication


    Alors que dans ta solution :

    1ère ligne :

    1 test +
    1 division +
    1 multiplication

    2ième ligne :

    partie gauche
    1 division +
    1 soustraction +
    1 test +

    partie droite
    1 addition


    Ce qui, comparé entre les 2, fait (1 division + 1 soustraction + 1 test) de plus dans ta solution que dans la mienne....




  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Août 2006
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 45
    Par défaut
    ok, you win...

  13. #13
    Membre chevronné Avatar de Pierre Maurette
    Profil pro
    Inscrit en
    Juillet 2002
    Messages
    283
    Détails du profil
    Informations personnelles :
    Âge : 69
    Localisation : France

    Informations forums :
    Inscription : Juillet 2002
    Messages : 283
    Par défaut
    Bonjour,

    Je me demande (c'est un euphémisme) si c'est bien de nommer votre fonction round(), qui existe dans <math.h> avec certes une interface différente.
    Vous devriez donner plus de précisions sur votre problème. Est-il "quelle est la routine la plus rapide en C/C++ pour calculer un arrondi à n décimales?" ou doit-on respecter le comportement de la fonction que vous proposez:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    inline double round(double dNum, double dStep)
    {
        double dVal = dStep * ((dStep > 0) ? (int)(dNum / dStep) : 0);
        if (dNum-dVal<dStep/2) return dVal;
        else return dVal + dStep;
    }
    quel que soit dStep ?

    Déjà, vous ne pouvez pas être plus lent en validant dStep > 0 dès l'acquisition de cette variable, avant la fonction.
    Vous pouvez ensuite réécrire le machin sans tests, ce qui à priori n'est pas toujours mieux qu'un test (et même parfois loin de là) mais ici sans doute mieux qu'un test à branches déséquilibrées. Enfin, c'est à voir ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    inline double round2(double dNum, double dStep)
    {
        return (dStep * (int)((dNum + (dStep / 2)) / dStep));
    }
    Facilement macrotable, comme ça on est sûr de l'inlinage (inlinitude, Ségolène ?) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define ROUND(x, y) ((y) * (int)(((x) + ((y) / 2)) / (y)))
    Maintenant, il me parait évident que si vous avez un tel souci d'optimisation, c'est que vous êtes dans une méchante boucle. Imaginons que vous ayez plusieurs niveaux de boucles, et que au dernier niveau seul dNum change, en d'autres termes que dStep change "peu souvent". Vous pouvez peut-être améliorer un peu en prémâchant le boulot à chaque changement de valeur de dStep:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    inline double round3(double dNum, double dStep, double demiStep, int invStep)
    {
        return (dStep * (int)((dNum + demiStep) * invStep));
    }
    ou (parce que là la non inlinitude serait dramatitude):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define ROUND(x, y, z, t) ((y) * (int)(((x) + (z)) * (t)))
    A vous de tester et de voir si un int est suffisant et si la multiplication par un entier est mieux que la division par un double.

    Enfin, il est possible que ne puissiez avoir qu'un nombre fini et raisonnable de valeurs de dStep, correspondant bien entendu à un nombre de décimales. Vous pourriez alors multipliquer la boucle en-dessous de l'acquisition de dStep et aiguiller selon la valeur de dStep. Vous ne pouvez pas vous contenter de multipliquer la fonction round() puiqu'en utilisant un tel tableau de fonctions, vous ne pourriez plus inliner. Vous auriez donc des fonctions du type:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    inline double round3(double dNum)
    {
        return (0.001 * (int)((dNum + 0.0005) * 1000));
    }
    ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define ROUND3(x) (0.001 * (int)(((x) + 0.0005) * 1000))
    Vous pouvez panacher, en ne codant en constantes que les cas les plus fréquent et en conservant une branche paramétrée, bref en utilisant ce que vous savez de vos données et que le compilateur ne peut savoir d'avance.

  14. #14
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Pierre Maurette
    beaucoup de choses avec lesquelles je suis d'accord
    Sauf l'introduction du cast (int) à la place du floor.

    Comme le paramètre de sortie est un double, faire un cast dans l'opération impose de re-faire un cast inverse pour effectuer la multiplication.

    Donc un peu plus lent (je pense, mais je ne suis pas sûr) que utiliser directement floor.

    D'un autre côté, si floor n'est pas définie comme un #define, il y a appel de fonction, donc plus lent...


  15. #15
    Membre averti
    Profil pro
    Inscrit en
    Août 2006
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 45
    Par défaut
    Merci à vous deux.

    Pour le moment je garde:

    inline double round(double dNum, double dStep)
    {
    return (dStep * (int)((dNum + (dStep / 2)) / dStep));
    }

    Il y a sans doute aussi possibilité de tout calculer en integer, shifter un bit plutôt que diviser dStep par deux, etc... Mais pas sûr d'y gagner en rapidité

    bv

  16. #16
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    * floor plutot que le cast permet que ca fonctionne aussi pour les nombres negatifs
    *a noter que ca arrondi a un multiple de dStep, mais que les valeurs probables de dStep risquent bien de n'etre qu'approximativement representees en flottant. Leurs inverses par contre ont des chances d'etre un entier.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return floor(num*part+0.5)/part;

Discussions similaires

  1. Réponses: 2
    Dernier message: 02/05/2008, 03h33
  2. Calcul rapide de percentiles
    Par benj63 dans le forum Algorithmes et structures de données
    Réponses: 6
    Dernier message: 08/12/2006, 14h50
  3. LES TECHNIQUES DES SGBDR / MySQL rapide ???
    Par SQLpro dans le forum Langage SQL
    Réponses: 1
    Dernier message: 12/09/2003, 11h16
  4. [VBA Excel] Effacer rapidement une feuille
    Par Invité dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 24/10/2002, 13h12

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