Question

J'ai besoin d'une simple fonction d'arrondi à virgule flottante, donc :

double round(double);

round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1

je peux trouver ceil() et floor() en maths.h - mais pas round().

Est-il présent dans la bibliothèque C++ standard sous un autre nom, ou est-il manquant ??

Était-ce utile?

La solution

Il n'y a pas de round () dans la bibliothèque standard C ++ 98. Vous pouvez cependant en écrire un vous-même. Ce qui suit est une implémentation de round-half-up :

double round(double d)
{
  return floor(d + 0.5);
}

La raison probable pour laquelle il n’existe pas de fonction round dans la bibliothèque standard C ++ 98 est qu’elle peut en fait être implémentée de différentes manières. Ce qui précède est une méthode courante, mais il en existe d’autres, telles que arrondi au niveau , qui est moins biaisé et généralement mieux si vous allez faire beaucoup d'arrondis; C’est un peu plus complexe à implémenter.

Autres conseils

Boost propose un ensemble simple de fonctions d'arrondi.

#include <boost/math/special_functions/round.hpp>

double a = boost::math::round(1.5); // Yields 2.0
int b = boost::math::iround(1.5); // Yields 2 as an integer

Pour plus d'informations, consultez le Documentation Boost.

Modifier:Depuis C++11, il existe std::round, std::lround, et std::llround.

La norme C ++ 03 s’appuie sur la norme C90 pour ce qu’elle appelle la bibliothèque standard C , qui est traitée dans le projet de norme C ++ 03 ( Le projet de norme public le plus proche de C ++ 03, accessible au public, est N1804 . ) section 1.2 Références normatives :

  

La bibliothèque décrite à l’article 7 de la norme ISO / IEC 9899: 1990 et à l’article 7 de la   ISO / IEC 9899 / Amd.1: 1995 est ci-après appelée la norme C   Bibliothèque. 1)

Si nous allons à la documentation C pour round, lround, llround on cppreference nous pouvons voir que round et les fonctions associées font partie de C99 et ne seront donc pas disponibles en C ++ 03 ou antérieur.

Dans C ++ 11, cela change, car C ++ 11 s'appuie sur le projet de norme C99 pour la bibliothèque standard C et fournit donc std :: round et pour les types de retour intégraux std :: lround, std :: llround :

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::round( 0.4 ) << " " << std::lround( 0.4 ) << " " << std::llround( 0.4 ) << std::endl ;
    std::cout << std::round( 0.5 ) << " " << std::lround( 0.5 ) << " " << std::llround( 0.5 ) << std::endl ;
    std::cout << std::round( 0.6 ) << " " << std::lround( 0.6 ) << " " << std::llround( 0.6 ) << std::endl ;
}

Une autre option également proposée par C99 serait std :: trunc , :

  

Calcule l'entier le plus proche dont la magnitude n'est pas supérieure à arg.

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::trunc( 0.4 ) << std::endl ;
    std::cout << std::trunc( 0.9 ) << std::endl ;
    std::cout << std::trunc( 1.1 ) << std::endl ;

}

Si vous devez prendre en charge des applications non C ++ 11, le mieux serait d’utiliser boost round, iround, lround, llround ou boost trunc .

Faire rouler votre propre version du tour est difficile

Rouler vous-même ne vaut probablement pas la peine, car Plus difficile qu'il n'y paraît: arrondir le flottant à l'entier le plus proche, partie 1 , Arrondir le nombre à virgule flottant au nombre entier le plus proche, partie 2 et Arrondir le flottant au nombre entier le plus proche, partie 3 expliquer:

Par exemple, un déploiement commun de votre implémentation avec std::floor et l'ajout de 0.5 ne fonctionne pas pour toutes les entrées:

double myround(double d)
{
  return std::floor(d + 0.5);
}

Une entrée pour laquelle cette opération échouera est 0.49999999999999994, ( voir en direct ).

Une autre implémentation courante consiste à convertir un type à virgule flottante en un type intégral, qui peut invoquer un comportement indéfini dans le cas où la partie intégrale ne peut pas être représentée dans le type de destination. Nous pouvons le voir dans le projet de section de la norme C ++ 4.9 Conversions flottantes-intégrales qui dit ( emphasis mine ):

  

Une prvalue de type virgule flottante peut être convertie en une prvalue de   type entier. La conversion tronque; c'est-à-dire la fraction   est jeté. Le comportement n'est pas défini si la valeur tronquée ne peut pas   être représenté dans le type de destination. [...]

Par exemple:

float myround(float f)
{
  return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}

Donné std::numeric_limits<unsigned int>::max() est 4294967295 alors l'appel suivant:

myround( 4294967296.5f ) 

provoquera un débordement, ( le voir en direct ) .

Nous pouvons voir à quel point c'est difficile en regardant cette réponse à la solution concise pour implémenter round () en C? qui fait référence à la version newlibs de float round simple précision. C'est une très longue fonction pour quelque chose qui semble simple. Il semble peu probable que quiconque sans connaissance intime de l'implication en virgule flottanteementations pourrait correctement implémenter cette fonction:

float roundf(x)
{
  int signbit;
  __uint32_t w;
  /* Most significant word, least significant word. */
  int exponent_less_127;

  GET_FLOAT_WORD(w, x);

  /* Extract sign bit. */
  signbit = w & 0x80000000;

  /* Extract exponent field. */
  exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;

  if (exponent_less_127 < 23)
    {
      if (exponent_less_127 < 0)
        {
          w &= 0x80000000;
          if (exponent_less_127 == -1)
            /* Result is +1.0 or -1.0. */
            w |= ((__uint32_t)127 << 23);
        }
      else
        {
          unsigned int exponent_mask = 0x007fffff >> exponent_less_127;
          if ((w & exponent_mask) == 0)
            /* x has an integral value. */
            return x;

          w += 0x00400000 >> exponent_less_127;
          w &= ~exponent_mask;
        }
    }
  else
    {
      if (exponent_less_127 == 128)
        /* x is NaN or infinite. */
        return x + x;
      else
        return x;
    }
  SET_FLOAT_WORD(x, w);
  return x;
}

Par contre, si aucune des autres solutions ne sont utilisables, newlib pourrait potentiellement être une option car il s'agit d'une implémentation bien testée.

Il convient de noter que si vous souhaitez obtenir un résultat entier à partir de l’arrondi, vous n’avez pas besoin de le transmettre par le plafond ou le plancher. Ie.,

int round_int( double r ) {
    return (r > 0.0) ? (r + 0.5) : (r - 0.5); 
}

Il est disponible depuis le C ++ 11 en cmath (selon http : //www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf )

#include <cmath>
#include <iostream>

int main(int argc, char** argv) {
  std::cout << "round(0.5):\t" << round(0.5) << std::endl;
  std::cout << "round(-0.5):\t" << round(-0.5) << std::endl;
  std::cout << "round(1.4):\t" << round(1.4) << std::endl;
  std::cout << "round(-1.4):\t" << round(-1.4) << std::endl;
  std::cout << "round(1.6):\t" << round(1.6) << std::endl;
  std::cout << "round(-1.6):\t" << round(-1.6) << std::endl;
  return 0;
}

Sortie:

round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2

Il est généralement implémenté en tant que floor(value + 0.5).

Modifier: et ce n’est probablement pas appelé arrondi car il ya au moins trois algorithmes d’arrondi que je connaisse: arrondir à zéro, arrondir au nombre entier le plus proche et arrondir de banquier. Vous demandez de arrondir à l'entier le plus proche.

Nous examinons 2 problèmes:

  1. arrondis des conversions
  2. conversion de type.

Arrondir les conversions signifie arrondir & # 177; float / double jusqu'au sol / plafond le plus proche float / double. Peut-être que votre problème se termine ici. Mais si vous devez retourner Int / Long, vous devez effectuer une conversion de type, et donc & Quot; Overflow & Quot; problème pourrait frapper votre solution. SO, faites une vérification d'erreur dans votre fonction

long round(double x) {
   assert(x >= LONG_MIN-0.5);
   assert(x <= LONG_MAX+0.5);
   if (x >= 0)
      return (long) (x+0.5);
   return (long) (x-0.5);
}

#define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
      error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

à partir de: http://www.cs.tut.fi/~jkorpela/ round.html

Un certain type d'arrondissement est également implémenté dans Boost:

#include <iostream>

#include <boost/numeric/conversion/converter.hpp>

template<typename T, typename S> T round2(const S& x) {
  typedef boost::numeric::conversion_traits<T, S> Traits;
  typedef boost::numeric::def_overflow_handler OverflowHandler;
  typedef boost::numeric::RoundEven<typename Traits::source_type> Rounder;
  typedef boost::numeric::converter<T, S, Traits, OverflowHandler, Rounder> Converter;
  return Converter::convert(x);
}

int main() {
  std::cout << round2<int, double>(0.1) << ' ' << round2<int, double>(-0.1) << ' ' << round2<int, double>(-0.9) << std::endl;
}

Notez que cela ne fonctionne que si vous effectuez une conversion en entier.

Vous pouvez arrondir à n chiffres avec la précision suivante:

double round( double x )
{
const double sd = 1000; //for accuracy to 3 decimal places
return int(x*sd + (x<0? -0.5 : 0.5))/sd;
}

Si vous souhaitez finalement convertir la double sortie de votre round() fonction en int, les solutions acceptées pour cette question se présenteront comme suit:

int roundint(double r) {
  return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}

Cela indique environ 8,88 & nbsp; ns sur ma machine lorsque les valeurs sont uniformément aléatoires.

Les informations ci-dessous sont fonctionnellement équivalentes, pour autant que je sache, mais la fréquence 2,48 & nbsp; ns sur ma machine offre un avantage significatif en termes de performances:

int roundint (double r) {
  int tmp = static_cast<int> (r);
  tmp += (r-tmp>=.5) - (r-tmp<=-.5);
  return tmp;
}

Parmi les raisons qui expliquent ces meilleures performances, citons le branchement ignoré.

Ces jours-ci, l'utilisation d'un compilateur C ++ 11, qui comprend une bibliothèque mathématique C99 / C ++ 11, ne devrait pas poser de problème. Mais alors la question devient: quelle fonction d'arrondi choisissez-vous?

C99 / C ++ 11 round() n'est souvent pas la fonction d'arrondi souhaitée . Il utilise un mode d'arrondi funky qui arrondit à 0 comme point de départ dans les cas à mi-parcours (+-xxx.5000). Si vous souhaitez spécifiquement ce mode d'arrondi ou si vous ciblez une implémentation C ++ où rint() est plus rapide que std::nearbyint(), utilisez-la (ou émulez son comportement avec l'une des autres réponses à cette question qui l'a pris au sérieux.) valeur et reproduit soigneusement ce comportement d’arrondi spécifique.)

L'arrondi de

std::rint() est différent de celui par défaut du arrondir au mode le plus proche, même si un tie-break . Le plus proche, même évite les biais statistiques dans l’amplitude moyenne des nombres, mais fait des biais vers les nombres pairs.

Deux fonctions d'arrondissement de la bibliothèque mathématique utilisent le mode d'arrondi par défaut actuel: std::round() et nearbyint , tous deux ajoutés C99 / C ++ 11, ils sont donc disponibles à tout moment nearbyint() est. La seule différence est que -ffast-math ne soulève jamais FE_INEXACT.

Préférez call pour des raisons de performances : gcc et clang les alignent plus facilement, mais gcc ne les alignent jamais roundsd (même avec roundpd)

gcc / clang pour x86-64 et AArch64

J'ai mis des fonctions de test sur l'explorateur de compilateur de Matt Godbolt , où vous pouvez voir le résultat source + asm (pour plusieurs compilateurs) . Pour plus d'informations sur la lecture de la sortie du compilateur, voir this Q & et ; et la conférence CppCon2017 de Matt: & # 8220; Qu'est-ce que mon compilateur a récemment effectué? Déboulonner le couvercle du compilateur & # 8221; ,

Dans le code de PF, c’est généralement une grosse victoire que d’intégrer de petites fonctions. Particulièrement sur les systèmes autres que Windows, où la convention d'appel standard ne comporte aucun registre préservé des appels. Le compilateur ne peut donc conserver aucune valeur de FP dans les registres XMM sur un vroundpd. Ainsi, même si vous ne connaissez pas vraiment asm, vous pouvez toujours facilement savoir s’il s’agit d’un simple appel de queue à la fonction de bibliothèque ou s’il est intégré à une ou deux instructions mathématiques. Tout ce qui est inséré dans une ou deux instructions est préférable à un appel de fonction (pour cette tâche particulière sur x86 ou ARM).

Sur x86, tout ce qui est intégré à SSE4.1 -msse4.1 peut être vectorisé automatiquement avec SSE4.1 -msse4.1 -ffast-math (ou AVX rint). (FP - & Gt; les conversions d'entiers sont également disponibles sous une forme SIMD compactée, à l'exception de FP - & Gt; entier de 64 bits nécessitant AVX512.)

  • std::rint:

    • x86 clang: insère une seule insn avec -ffast-math -msse4.1.
    • x86 gcc: insère une seule insn uniquement avec std::round et uniquement sur gcc 5.4 et plus tôt . Plus tard, gcc ne l’aligne jamais (peut-être ne se sont-ils pas rendu compte que l’un des bits immédiats pouvait supprimer l’exception inexacte? C’est ce que clang utilise, mais le gcc plus ancien utilise le même effet immédiat que pour std::floor quand il le fait en ligne)
    • AArch64 gcc6.3: insère une seule insn par défaut.
  • std::ceil:

    • x86 clang: insère une seule insn avec std::trunc
    • x86 gcc7: insère une seule insn avec int. (Sans SSE4.1, plusieurs instructions en ligne)
    • x86 gcc6.x et versions antérieures: insère une seule insn avec long.
    • AArch64 gcc: insère une seule insn par défaut
  • long long:

    • x86 clang: ne pas aligner
    • x86 gcc: insère plusieurs instructions avec lrint, nécessitant deux constantes de vecteur.
    • AArch64 gcc: insère une seule instruction (prise en charge matérielle pour ce mode d'arrondi, ainsi que par défaut pour IEEE et la plupart des autres.)
  • llrint / int i = lrint(x) / float

    • x86 clang: insère une seule insn avec double
    • x86 gcc7.x: insère une seule insn avec INT_MIN
    • x86 gcc6.x et versions antérieures: insère une seule insn avec LLONG_MIN
    • AArch64 gcc: insère par défaut une seule instruction

Arrondi à 0x8000000 / cvttsd2si / cvtsd2si:

Vous avez deux options ici: utilisez unsigned (comme (int/long)rint mais renvoie <=> ou <=> pour <=>), ou utilisez une fonction d'arrondi FP - > FP, puis convertissez en un nombre entier, tapez normalement (avec troncature). Certains compilateurs optimisent un chemin mieux que l’autre.

long l = lrint(x);

int  i = (int)rint(x);

Notez que <=> convertit <=> ou <=> - > <=> d'abord, puis tronque l'entier en <=>. Cela fait une différence pour les entiers hors limites: Comportement non défini en C ++, mais bien défini pour le FP x86 - & Gt; instructions int (que le compilateur émettra à moins qu'il ne voie UB lors de la compilation en effectuant une propagation constante, il est alors autorisé à créer du code qui se casse s'il est exécuté).

Sur x86, une conversion de FP - > entier qui déborde de l’entier produit <=> ou <=> (un motif binaire de <=> ou l’équivalent 64 bits, avec uniquement le bit de signature) ensemble). Intel appelle cela le & "Entier indéfini &"; valeur. (Voir l'entrée manuelle <=> , l'instruction SSE2 qui convertit (avec troncature) scalar double en entier signé. Il est disponible avec la destination entière 32 bits ou 64 bits (en mode 64 bits uniquement). Il existe également un <=> (conversion avec le mode d'arrondi en cours), qui correspond à ce que nous aimerions que le compilateur émette. , mais malheureusement, gcc et clang ne le feront pas sans <=>.

Notez également que les FP vers / depuis <=> int / long est moins efficace sur x86 (sans AVX512). La conversion en 32 bits non signés sur une machine 64 bits est relativement peu coûteuse; il suffit de convertir en 64 bits signé et tronqué. Mais sinon, c'est beaucoup plus lent.

  • x86 clang avec / sans <=>: <=> en ligne vers <=> / <=>. (optimisation manquée à <=>). <=> n'est pas du tout en ligne.

  • x86 gcc6.x et versions antérieures sans <=>: aucune entrée de ligne

  • x86 gcc7 sans <=>: <=> arrondit et convertit séparément (avec 2 instructions totales de SSE4.1 activée, sinon avec un tas de code en ligne pour <=> sans <=>). <=> n'est pas en ligne.
  • x86 gcc avec <=>: tous alignés vers <=> (optimal) , aucun besoin de SSE4.1.

  • AArch64 gcc6.3 sans <=>: <=> inlines à 2 instructions. <=> n'est pas intégré

  • AArch64 gcc6.3 avec <=>: <=> est compilé en un appel à <=>. <=> n'est pas en ligne. Il peut s’agir d’une optimisation manquée à moins que les deux instructions que nous obtenons sans <=> soient très lentes.

Faites attention à floor(x+0.5). Voici ce qui peut arriver pour les nombres impairs dans la plage [2 ^ 52,2 ^ 53]:

-bash-3.2$ cat >test-round.c <<END

#include <math.h>
#include <stdio.h>

int main() {
    double x=5000000000000001.0;
    double y=round(x);
    double z=floor(x+0.5);
    printf("      x     =%f\n",x);
    printf("round(x)    =%f\n",y);
    printf("floor(x+0.5)=%f\n",z);
    return 0;
}
END

-bash-3.2$ gcc test-round.c
-bash-3.2$ ./a.out
      x     =5000000000000001.000000
round(x)    =5000000000000001.000000
floor(x+0.5)=5000000000000002.000000

Il s'agit de http://bugs.squeak.org/view.php?id = 7134 . Utilisez une solution semblable à celle de @konik.

Ma propre version robuste serait quelque chose comme:

double round(double x)
{
    double truncated,roundedFraction;
    double fraction = modf(x, &truncated);
    modf(2.0*fraction, &roundedFraction);
    return truncated + roundedFraction;
}

Une autre raison d'éviter le plancher (x + 0,5) est donnée, ici .

Il n’est pas nécessaire de mettre en œuvre quoi que ce soit, je ne suis donc pas sûr de savoir pourquoi tant de réponses impliquent des définitions, des fonctions ou des méthodes.

En C99

Nous avons ce qui suit et et en-tête < tgmath.h > pour les macros de type générique.

#include <math.h>
double round (double x);
float roundf (float x);
long double roundl (long double x);

Si vous ne pouvez pas compiler cela, vous avez probablement laissé de côté la bibliothèque mathématique. Une commande similaire à celle-ci fonctionne sur tous les compilateurs C que j’ai (plusieurs).

gcc -lm -std=c99 ...

En C ++ 11

Nous avons les surcharges suivantes et supplémentaires dans #include < cmath > qui reposent sur la virgule flottante double précision IEEE.

#include <math.h>
double round (double x);
float round (float x);
long double round (long double x);
double round (T x);

Il existe également des équivalents dans l'espace de noms standard .

Si vous ne pouvez pas compiler cela, vous utilisez peut-être une compilation C au lieu de C ++. La commande de base suivante ne génère ni erreur ni avertissement avec g ++ 6.3.1, x86_64-w64-mingw32-g ++ 6.3.0, clang-x86_64 ++ 3.8.0 et la communauté Visual C ++ 2015.

g++ -std=c++11 -Wall

Avec division ordinale

Lorsque vous divisez deux nombres ordinaux, où T est court, entier, long ou un autre ordinal, l'expression arrondie est la suivante.

T roundedQuotient = (2 * integerNumerator + 1)
    / (2 * integerDenominator);

Précision

Il ne fait aucun doute que des inexactitudes étranges apparaissent dans les opérations en virgule flottante, mais cela ne se produit que lorsque les chiffres apparaissent et a peu à voir avec l'arrondi.

La source n'est pas simplement le nombre de chiffres significatifs dans la mantisse de la représentation IEEE d'un nombre à virgule flottante, elle est liée à notre pensée décimale en tant qu'être humain.

Dix est le produit de cinq et deux, et 5 et 2 sont relativement premiers. Par conséquent, les normes à virgule flottante IEEE ne peuvent pas être parfaitement représentées sous forme de nombres décimaux pour toutes les représentations numériques binaires.

Ce n'est pas un problème avec les algorithmes d'arrondi. C'est la réalité mathématique qu'il convient de prendre en compte lors de la sélection des types et de la conception des calculs, de la saisie des données et de l'affichage des nombres. Si une application affiche les chiffres indiquant ces problèmes de conversion décimale-binaire, elle exprime de manière visuelle une précision qui n'existe pas dans la réalité numérique et qui devrait être modifiée.

Fonction double round(double) avec l'utilisation de la modf fonction:

double round(double x)
{
    using namespace std;

    if ((numeric_limits<double>::max() - 0.5) <= x)
        return numeric_limits<double>::max();

    if ((-1*std::numeric_limits<double>::max() + 0.5) > x)
        return (-1*std::numeric_limits<double>::max());

    double intpart;
    double fractpart = modf(x, &intpart);

    if (fractpart >= 0.5)
        return (intpart + 1);
    else if (fractpart >= -0.5)
        return intpart;
    else
        return (intpart - 1);
    }

Pour être compilé proprement, inclut " math.h " et " limites " sont nécessaires. La fonction fonctionne selon le schéma d'arrondi suivant:

  • le tour de 5.0 est 5.0
  • round de 3.8 est 4.0
  • tour de 2.3 est 2.0
  • tour de 1,5 est 2.0
  • la ronde de 0,501 vaut 1,0
  • tour de 0.5 est 1.0
  • tour de 0.499 est 0.0
  • round of 0.01 is 0.0
  • tour de 0.0 est 0.0
  • tour de -0,01 est -0,0
  • le tour de -0,499 est égal à -0,0
  • tour de -0.5 est -0.0
  • tour de -0.501 est -1.0
  • tour de -1.5 est -1.0
  • tour de -2,3 est -2,0
  • tour de -3.8 est -4.0
  • tour de -5.0 est -5.0

Si vous avez besoin de pouvoir compiler du code dans des environnements prenant en charge la norme C ++ 11, mais également de pouvoir compiler le même code dans des environnements qui ne le prennent pas en charge, vous pouvez utiliser une macro de fonction pour: Choisissez entre std :: round () et une fonction personnalisée pour chaque système. Passez simplement -DCPP11 ou /DCPP11 au compilateur compatible C ++ 11 (ou utilisez ses macros de version intégrées) et créez un en-tête comme celui-ci:

// File: rounding.h
#include <cmath>

#ifdef CPP11
    #define ROUND(x) std::round(x)
#else    /* CPP11 */
    inline double myRound(double x) {
        return (x >= 0.0 ? std::floor(x + 0.5) : std::ceil(x - 0.5));
    }

    #define ROUND(x) myRound(x)
#endif   /* CPP11 */

Pour un exemple rapide, voir http://ideone.com/zal709 .

Ceci correspond approximativement à std :: round () dans les environnements non compatibles avec C ++ 11, y compris la préservation du bit de signe pour -0.0. Toutefois, cela peut entraîner de légères pertes de performances et vous aurez probablement des problèmes pour arrondir certains & "; Problèmes &" Connus; valeurs en virgule flottante telles que 0,4999999999999999994 ou des valeurs similaires.

Sinon, si vous avez accès à un compilateur compatible C ++ 11, vous pouvez simplement récupérer std :: round () dans son en-tête <cmath> et l'utiliser pour créer votre propre en-tête qui définit la fonction si c'est pas déjà défini. Notez toutefois que cette solution peut ne pas être optimale, surtout si vous devez compiler pour plusieurs plates-formes.

D'après la réponse de Kalaxy, voici une solution basée sur un modèle qui arrondit tout nombre à virgule flottante au type entier le plus proche, en fonction de l'arrondissement naturel. Il génère également une erreur en mode débogage si la valeur est hors de portée du type entier, servant ainsi approximativement de fonction de bibliothèque viable.

    // round a floating point number to the nearest integer
    template <typename Arg>
    int Round(Arg arg)
    {
#ifndef NDEBUG
        // check that the argument can be rounded given the return type:
        if (
            (Arg)std::numeric_limits<int>::max() < arg + (Arg) 0.5) ||
            (Arg)std::numeric_limits<int>::lowest() > arg - (Arg) 0.5)
            )
        {
            throw std::overflow_error("out of bounds");
        }
#endif

        return (arg > (Arg) 0.0) ? (int)(r + (Arg) 0.5) : (int)(r - (Arg) 0.5);
    }

Comme indiqué dans les commentaires et autres réponses, la bibliothèque standard ISO C ++ n'ajoutait pas round() jusqu'à ISO C ++ 11, lorsque cette fonction a été utilisée par référence à la bibliothèque mathématique standard ISO C99.

Pour les opérandes positifs dans [& # 189 ;, ub ] round(x) == floor (x + 0.5), où ub vaut 2 23 pour <= > lorsqu’il est mappé sur IEEE-754 (2008) float et 2 52 pour binary32 lorsqu’il est mappé sur IEEE-754 (2008) double. Les nombres 23 et 52 correspondent au nombre de bits de mantisse stockés dans ces deux formats à virgule flottante. Pour les opérandes positifs dans [+0, & # 189;) binary64, et pour les opérandes positifs dans ( ub , + & # 8734;] round(x) == 0. symétriques par rapport à l'axe des x, les arguments négatifs round(x) == x peuvent être traités selon x.

Ceci mène au code compact ci-dessous. Il compile un nombre raisonnable d'instructions machine sur différentes plates-formes. J'ai observé le code le plus compact sur les GPU, où round(-x) == -round(x) nécessite environ une douzaine d'instructions. En fonction de l'architecture du processeur et de la chaîne d'outils, cette approche à virgule flottante peut être plus rapide ou plus lente que l'implémentation à base d'entiers de newlib référencée dans un réponse différente .

J'ai testé my_roundf() de manière exhaustive contre la nouvelle implémentation newlib roundf() utilisant le compilateur Intel version 13, avec /fp:strict et /fp:fast. J'ai également vérifié que la version de newlib correspond à la mathimf dans la <=> bibliothèque du compilateur Intel. Un test exhaustif n’est pas possible pour la double précision <=>, mais le code est structurellement identique à l’implémentation simple précision.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

float my_roundf (float x)
{
    const float half = 0.5f;
    const float one = 2 * half;
    const float lbound = half;
    const float ubound = 1L << 23;
    float a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floorf (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

double my_round (double x)
{
    const double half = 0.5;
    const double one = 2 * half;
    const double lbound = half;
    const double ubound = 1ULL << 52;
    double a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floor (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

uint32_t float_as_uint (float a)
{
    uint32_t r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float uint_as_float (uint32_t a)
{
    float r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float newlib_roundf (float x)
{
    uint32_t w;
    int exponent_less_127;

    w = float_as_uint(x);
    /* Extract exponent field. */
    exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;
    if (exponent_less_127 < 23) {
        if (exponent_less_127 < 0) {
            /* Extract sign bit. */
            w &= 0x80000000;
            if (exponent_less_127 == -1) {
                /* Result is +1.0 or -1.0. */
                w |= ((uint32_t)127 << 23);
            }
        } else {
            uint32_t exponent_mask = 0x007fffff >> exponent_less_127;
            if ((w & exponent_mask) == 0) {
                /* x has an integral value. */
                return x;
            }
            w += 0x00400000 >> exponent_less_127;
            w &= ~exponent_mask;
        }
    } else {
        if (exponent_less_127 == 128) {
            /* x is NaN or infinite so raise FE_INVALID by adding */
            return x + x;
        } else {
            return x;
        }
    }
    x = uint_as_float (w);
    return x;
}

int main (void)
{
    uint32_t argi, resi, refi;
    float arg, res, ref;

    argi = 0;
    do {
        arg = uint_as_float (argi);
        ref = newlib_roundf (arg);
        res = my_roundf (arg);
        resi = float_as_uint (res);
        refi = float_as_uint (ref);
        if (resi != refi) { // check for identical bit pattern
            printf ("!!!! arg=%08x  res=%08x  ref=%08x\n", argi, resi, refi);
            return EXIT_FAILURE;
        }
        argi++;
    } while (argi);
    return EXIT_SUCCESS;
}

J'utilise l'implémentation suivante de round in asm pour l'architecture x86 et le C ++ spécifique à MS VS:

__forceinline int Round(const double v)
{
    int r;
    __asm
    {
        FLD     v
        FISTP   r
        FWAIT
    };
    return r;
}

UPD: pour retourner une double valeur

__forceinline double dround(const double v)
{
    double r;
    __asm
    {
        FLD     v
        FRNDINT
        FSTP    r
        FWAIT
    };
    return r;
}

Sortie:

dround(0.1): 0.000000000000000
dround(-0.1): -0.000000000000000
dround(0.9): 1.000000000000000
dround(-0.9): -1.000000000000000
dround(1.1): 1.000000000000000
dround(-1.1): -1.000000000000000
dround(0.49999999999999994): 0.000000000000000
dround(-0.49999999999999994): -0.000000000000000
dround(0.5): 0.000000000000000
dround(-0.5): -0.000000000000000

Meilleure façon d'arrondir une valeur flottante par " n " décimales, est comme suit avec dans le temps O (1): -

Nous devons arrondir la valeur par 3 positions, c.-à-d. n = 3.Alors,

float a=47.8732355;
printf("%.3f",a);
// Convert the float to a string
// We might use stringstream, but it looks like it truncates the float to only
//5 decimal points (maybe that's what you want anyway =P)

float MyFloat = 5.11133333311111333;
float NewConvertedFloat = 0.0;
string FirstString = " ";
string SecondString = " ";
stringstream ss (stringstream::in | stringstream::out);
ss << MyFloat;
FirstString = ss.str();

// Take out how ever many decimal places you want
// (this is a string it includes the point)
SecondString = FirstString.substr(0,5);
//whatever precision decimal place you want

// Convert it back to a float
stringstream(SecondString) >> NewConvertedFloat;
cout << NewConvertedFloat;
system("pause");

C’est peut-être un sale moyen de conversion inefficace, mais bon, ça marche lol. Et c'est bien, parce que cela s'applique au flotteur réel. Pas seulement affecter la sortie visuellement.

J'ai fait ceci:

#include <cmath.h>

using namespace std;

double roundh(double number, int place){

    /* place = decimal point. Putting in 0 will make it round to whole
                              number. putting in 1 will round to the
                              tenths digit.
    */

    number *= 10^place;
    int istack = (int)floor(number);
    int out = number-istack;
    if (out < 0.5){
        floor(number);
        number /= 10^place;
        return number;
    }
    if (out > 0.4) {
        ceil(number);
        number /= 10^place;
        return number;
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top