Partage
  • Partager sur Facebook
  • Partager sur Twitter

Arrondi au plus proche sans utiliser de float

    25 novembre 2021 à 0:37:17

    Bonjour,

    Je recherche s'il existe une façon d'écrire une division d'entier, avec un résultat arrondi au plus proche, sans que ce ne soit des float.

    C'est quelque chose je que faisais auparavant en assembleur sur PIC16F... . Les routines étaient qualifiées de "double précision".

    En langage C, C++ mes recherches sur internet me conduisent systématiquement sur solutions qui concernent des float.

    Exemple : 20 / 3 = 6,666...

    La division d'entier donne un résultat de 6, là où je voudrais un résultat arrondi au plus proche qui ferait 7.

    Une solution pour faire ça en C :

        1. Faire la division en doublant le numérateur: ici 40 / 3

        2. Si le bit 0 du résultat est à 1 : ajouter 1, sinon ne rien faire

        3. Rotation d'un rang vers la droite pour diviser par 2 le résultat

     Comme quoi il est possible et utile d'avoir un résultat arrondi au plus proche sur une division d'entiers.

    Quelqu'un saurait-il s'il existe une fonction toute faite pour cela ?

    • Partager sur Facebook
    • Partager sur Twitter
      25 novembre 2021 à 1:04:52

      Il n'y pas de fonction dans la librairie standard pour faire ce que tu veux à ma connaissance, mais une fonction retournant (dividende + diviseur / 2) / diviseur devrait répondre au besoin je pense.
      • Partager sur Facebook
      • Partager sur Twitter
        25 novembre 2021 à 2:54:37

        Et ceci ferait ton affaire?
        valeur = (nombre * 2 + diviseur) / (diviseur * 2);
        Pour 19/3, j'obtiens 6, et pour 20/3, j'obtiens 7

        Si tu veux arrondir à l'entier suivant:
        valeur = (nombre + diviseur - 1) / diviseur;
        Pour 19/3 et 20/3 tu obtiendra 7.

        -
        Edité par PierrotLeFou 25 novembre 2021 à 3:08:40

        • Partager sur Facebook
        • Partager sur Twitter

        Le Tout est souvent plus grand que la somme de ses parties.

          25 novembre 2021 à 21:19:28

          Je vous remercie pour vos réponses, donc apparemment il n'y a pas de fonction dans la librairie standard :(. Je vais donc faire la fonction.

          Merci pour ces solutions proposées, je pense quelles sont toutes bonnes.

          Par soucis d'optimisation (microcontrôleur 8 bits) je vais faire avec celle que j'ai citée, donnant quelque chose comme ceci en 16bits nons signés :


          uint16_t      Div16bitsDP ( uint16_t  numerateur , uint16_t  denominateur )  {

          uint16_t  resultat = (  2 * numerateur)  / denominateur ;

          if (  ( resultat & 0b1 )  ==  0b1 )            resultat ++ ;

          resultat >>= 1 ;

          return  resultat  ;

          }



          • Partager sur Facebook
          • Partager sur Twitter
            26 novembre 2021 à 2:11:52

            En pseudo-assembleur:
                load A,(nombre)
                load B,(diviseur)
                lshift,A,1   décaler A de un vers la gauche
                add A,B   ajouter B à A
                lshift B,1
                div A=A/B   diviser A par B
            Je pense que les compilateurs optimisent la multiplication par 2 par un décalage de 1 vers la gauche.
            Sinon, tu peux écrire  numerateur <<= 1;
            Dans ton code comme le mien, on doit faire une division. C'est en général l'instruction la plus longue.
            Tu fais un test et un masque que je ne fais pas.
            • Partager sur Facebook
            • Partager sur Twitter

            Le Tout est souvent plus grand que la somme de ses parties.

              26 novembre 2021 à 10:35:02

              oui PierrotLeFou , votre solution semble effectivement plus optimale en nombre d'opération.

              Je préfère aussi l'opérateur << pour la multiplication par 2, on est pas sûr du comportement du compilateur.

              Et même si en fait le microcontroleur mega 2560 que j'utilise, à ma grande surprise est capable de faire une multiplication en un seul cycle (sur 8 bits).

              Je serais curieux de savoir comme c'est câblé ...


              ce qui me donne la fonction (sauf erreur) :

              uint32_t      Div32bitsDP ( uint32_t dividende , uint32_t diviseur ) {

               return  ( ( ( dividende << 1 ) + diviseur ) / ( diviseur << 1 ) ) ;

              }


              Merci bien

              • Partager sur Facebook
              • Partager sur Twitter
                26 novembre 2021 à 11:38:31

                Il est certain que le compilateur va optimiser une division par 2. C'est trivial pour lui. A mon avis la solution que je t'ai donnée est plus optimale (un bitshift en moins).
                • Partager sur Facebook
                • Partager sur Twitter
                  26 novembre 2021 à 15:18:04

                  @SpaceIn:
                  J'ai testé expérimentalement ta formule vs la mienne. Les deux donnent le même résultat pour 1 <= nombre <= 1000 et 1 <= diviseur <= nombre/2
                  Donc, effectivement ta formule est plus efficace
                  • Partager sur Facebook
                  • Partager sur Twitter

                  Le Tout est souvent plus grand que la somme de ses parties.

                    28 novembre 2021 à 22:17:31

                    SpaceIn

                    J'avais un doute d'une éventuelle perte de précision en faisant diviseur / 2 ( ou diviseur >> 1 ).

                    J'ai essayé sur papier de trouver des cas critiques avec un diviseur impair, je n'en ai pas trouvé.

                     Effectivement c'est une meilleur formule, je vous remercie. 

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Arrondi au plus proche sans utiliser de float

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    • Editeur
                    • Markdown