round () pour float en C++

j'ai besoin d'une simple fonction d'arrondi en virgule flottante, donc:

double round(double);

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

je peux trouver ceil() et floor() dans les mathématiques.h-mais pas round() .

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

219
demandé sur Post Self 2009-01-28 01:06:30

20 réponses

il n'y a pas de round() dans la bibliothèque standard c++98. Vous pouvez écrire vous-même. Ce qui suit est une mise en œuvre de rond-moitié :

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

la raison probable pour laquelle il n'y a pas de fonction ronde 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 voie commune, mais il y en a d'autres comme round-to-pair , qui est moins biaisé et généralement mieux si vous allez faire beaucoup de l'arrondissement; c'est un peu plus complexe à mettre en œuvre.

136
répondu Andreas Magnusson 2017-09-27 09:16:53

Boost offre 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, voir la Boost documentation .

Edit : depuis C++11, Il y a std::round , std::lround , et std::llround .

92
répondu Daniel Wolf 2015-09-25 20:08:15

la norme C++03 s'appuie sur la norme C90 pour ce que la norme appelle la bibliothèque C Standard qui est couverte dans le projet de norme C++03 ( projet de norme disponible au public le plus proche de C++03 est N1804 "1519180920 ) section 1.2 Références normatives :

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

si nous allons à la c documentation for round, lround, llround on cppreference nous pouvons voir que round et les fonctions connexes font partie de c99 et ne seront donc pas disponibles en C++03 ou avant.

en C++11 cela change depuis que C++11 repose sur le C99 projet de norme pour c bibliothèque standard et fournit donc std:: rond et pour les types de retour intégral 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 de C99 serait std:: trunc qui:

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 avez besoin de prendre en charge les applications non C++11 votre meilleur pari serait d'utiliser boost round, iround, lround, llround ou boost trunc .

Rouler votre propre version de ronde est dur

rouler le sien ne vaut probablement pas l'effort de plus dur qu'il n'y paraît: flotteur arrondissant à l'entier le plus proche, partie 1 , flotteur arrondissant à l'entier le plus proche, partie 2 et Arrondi à flotter à la plus proche entier, partie 3 , expliquent:

par exemple un rouleau courant votre implémentation en utilisant std::floor et en ajoutant 0.5 ne fonctionne pas pour toutes les entrées:

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

une entrée qui échouera Pour is 0.49999999999999994 , ( voir en direct ).

une autre mise en œuvre courante consiste à lancer une type de point à un type intégral, qui peut invoquer un comportement non dé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 norme C++ section 4.9 conversions intégrales flottantes qui dit ( emphasis mine ):

Un prvalue d'un type à virgule flottante peuvent être convertis à un prvalue d'un type entier. La conversion tronque; que est, la partie fractionnaire est mis au rebut. 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 puis l'appel suivant:

myround( 4294967296.5f ) 

va provoquer un débordement, ( voir en direct ).

nous pouvons voir à quel point c'est vraiment difficile en regardant cette réponse à façon Concise de mettre en œuvre round() en C? qui fait référence à newlibs version d'un flotteur de précision simple rond. C'est une fonction très longue pour quelque chose qui semble simple. Il semble peu probable que quelqu'un qui n'a pas une connaissance intime des implémentations en virgule flottante puisse correctement mettre en œuvre 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;
}

sur l'autre main si aucune des autres solutions sont utilisables newlib pourrait être une option, car elle est un bien testé la mise en œuvre.

73
répondu Shafik Yaghmour 2017-05-23 12:10:11

il peut être intéressant de noter que si vous vouliez un résultat entier de l'arrondissement, vous n'avez pas besoin de passer par ceil ou le plancher. C'est-à-dire:

int round_int( double r ) {
    return (r > 0.0) ? (r + 0.5) : (r - 0.5); 
}
72
répondu kalaxy 2011-01-11 16:45:51

il est disponible depuis 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
39
répondu schibum 2012-06-19 13:23:09

il est généralement mis en œuvre comme floor(value + 0.5) .

Edit: et ce n'est probablement pas appelé rond car il y a au moins trois algorithmes d'arrondissement que je connais: rond à zéro, rond à l'entier le plus proche, et l'Arrondissement de banquier. Vous demandez un entier rond à plus proche.

28
répondu MSN 2012-07-03 14:57:53

il y a 2 problèmes que nous examinons:

    "151940920 arrondissement des conversions Conversion de type
  1. .

les conversions D'arrondi signifient arrondi ± flottant/double au plancher le plus proche/flotteur de ceil/double. Peut-être que votre problème se termine ici. Mais si on s'attend à ce que vous retourniez Int/Long, vous devez effectuer la conversion de type, et donc le problème de "débordement" pourrait frapper votre solution. Ainsi, faites une vérification pour l'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))

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

13
répondu Sangeet 2010-06-29 12:37:29

un certain type d'arrondissement est également mis en œuvre 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 faites une conversion à-Entier.

11
répondu Philipp 2010-06-29 13:02:15

vous pouvez arrondir à n chiffres de précision avec:

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;
}
6
répondu Carl 2010-02-12 03:02:23

si vous voulez finalement convertir la sortie double de votre fonction round() en une fonction int , alors les solutions acceptées de cette question ressembleront à quelque chose comme:

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

cette horloge autour de 8,88 ns sur ma machine lorsqu'elle est transmise en valeurs aléatoires uniformes.

ci-dessous est fonctionnellement équivalent, pour autant que je puisse dire, mais horloges à 2,48 ns sur mon machine, pour un avantage de performance significatif:

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

parmi les raisons pour lesquelles la meilleure performance est la branche sautée.

5
répondu dshin 2017-12-03 03:02:31

méfiez-vous de floor(x+0.5) . Voici ce qui peut arriver pour les nombres impairs dans la gamme [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

C'est http://bugs.squeak.org/view.php?id=7134 . Utiliser une solution comme 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 .

4
répondu aka.nice 2017-12-03 03:00:31

il n'y a pas besoin d'implémenter quoi que ce soit, donc je ne sais pas pourquoi tant de réponses impliquent des définitions, des fonctions ou des méthodes.

C99

Nous avons et de et en-tête pour les macros génériques de type.

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

si vous ne pouvez pas compiler ceci, vous avez probablement oublié la bibliothèque de mathématiques. Une commande similaire à celle-ci fonctionne sur chaque compilateur C que j'ai (plusieurs).

gcc -lm -std=c99 ...

In C++11

nous avons les surcharges suivantes dans #include < cmath> qui dépendent de la double précision IEEE floating point.

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

il y a equivalents dans l'espace de noms std aussi.

si vous ne pouvez pas compiler ceci, vous pouvez utiliser c compilation au lieu de C++. La commande de base suivante ne produit ni erreurs ni Avertissements avec g++ 6.3.1, x86_64-w64-mingw32-g++ 6.3.0, clang-x86_64++ 3.8.0, et Visual C++ 2015 Community.

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

Avec Division Ordinale

en divisant deux nombres ordinaux, où T est court, int, long, ou un autre ordinal, l'expression arrondie est celle-ci.

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

Précision

il ne fait aucun doute que des inexactitudes à l'apparence étrange apparaissent dans opérations ponctuelles, mais ce n'est que lorsque les chiffres apparaissent, et a peu à voir avec l'arrondissement.

la source n'est pas seulement le nombre de chiffres significatifs dans le mantissa de la représentation IEEE d'un nombre à virgule flottante, il est lié à notre pensée décimale en tant qu'humains.

Ten est le produit de cinq et deux, et 5 et 2 sont relativement premiers. Par conséquent, les normes à virgule flottante de L'IEEE ne peuvent pas être représentées parfaitement en tant que valeurs décimales. nombres 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 qui doit être prise 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 qui montrent ces décimales binaires problèmes de conversion, alors l'application exprime visuellement la précision qui n'existe pas dans la réalité numérique et doit être modifié.

2
répondu FauChristian 2017-02-04 09:13:16

de nos jours, il ne devrait pas être un problème d'utiliser un compilateur C++11 qui inclut une bibliothèque de mathématiques c99/C++11. Mais alors la question devient: quelle fonction d'arrondi choisissez-vous?

c99 / C++11 round() n'est souvent pas la fonction d'arrondissement que vous voulez . Il utilise un mode d'arrondi funky qui tourne à partir de 0 comme un tie-break sur les cas à mi-chemin ( +-xxx.5000 ). Si vous ne souhaitez que des modes d'arrondi, ou vous ciblez un C++ où round() est plus rapide que rint() , puis l'utiliser (ou imiter son comportement avec l'une des autres réponses à cette question qui l'a pris au pied de la lettre et a soigneusement reproduit ce comportement d'arrondi spécifique.)

round() arrondissement est différente de la IEEE754 par défaut tour la plus proche de mode avec la même comme un tie-break . Plus proche-Evite même le biais statistique dans l'ampleur moyenne des nombres, mais ne biais vers des nombres pairs.

il y a deux fonctions d'Arrondissement de bibliothèque de mathématiques qui utilisent le mode d'arrondissement par défaut courant: std::nearbyint() et std::rint() , tous les deux ajoutés en C99 / C++11, de sorte qu'ils sont disponibles à tout moment std::round() est. La seule différence est que nearbyint ne soulève jamais FE_INEXACT.

Préfèrent rint() pour des raisons de performances : gcc et clang les deux inline it plus facilement, mais gcc jamais inlines nearbyint() (même avec -ffast-math )


gcc/clang pour x86-64 et AArch64

j'ai mis quelques fonctions de test sur Matt Godbolt du Compilateur Explorer , où vous pouvez voir la source + sortie asm (pour plusieurs compilateurs). Pour en savoir plus sur la lecture de la sortie de compilateur, voir ce Q & r , et Cppcon2017 de Matt talk: " Qu'est-ce que mon compilateur a fait pour moi dernièrement? Déboulonner le couvercle du compilateur" ,

en code FP, c'est généralement une grande victoire pour les petites fonctions en ligne. Particulièrement sur les non-fenêtres, où la convention d'appel standard n'a pas de registres protégés par les appels, de sorte que le compilateur ne peut pas conserver de valeurs FP dans les registres XMM à travers un call . Donc, même si vous ne connaissez pas vraiment asm, vous pouvez toujours facilement voir si c'est juste un coup de fil à la fonction de bibliothèque ou si elle inline à une ou deux instructions mathématiques. Tout ce qui est relié à une ou deux instructions est mieux qu'un appel de fonction (pour cette tâche particulière sur x86 ou ARM).

sur x86, tout ce qui est connecté à SSE4.1 roundsd peut auto-vectoriser avec SSE4.1 roundpd (ou AVX vroundpd ). (FP - >entier conversions sont également disponibles dans la forme emballée SIMD, à l'exception de FP->64-bit entier qui nécessite AVX512.)

  • std::nearbyint() :

    • x86 clang: inlines à un seul insn avec -msse4.1 .
    • x86 gcc: ligne vers un seul insn seulement avec -msse4.1 -ffast-math , et seulement sur gcc 5.4 et avant . Plus tard, gcc ne l'a jamais allégé (peut-être n'ont-ils pas réalisé que l'un des bits immédiats peut supprimer l'exception inexacte? C'est ce que clang utilise, mais l'ancien gcc utilise le même comme pour rint quand il ne inline)
    • AArch64 gcc6.3: inlines à un seul insn par défaut.
  • std::rint :

    • x86 clang: inlines à un seul insn avec -msse4.1
    • x86 gcc7: inlines à un seul insn avec -msse4.1 . (Sans SSE4.1, inlines de plusieurs instructions)
    • x86 gcc6.x et plus tôt: inlines à un seul insn avec -ffast-math -msse4.1 .
    • AArch64 gcc: inlines à un seul insn par défaut
  • std::round :

    • x86 clang: n'a pas d'associé
    • x86 gcc: en ligne vers plusieurs instructions avec -ffast-math -msse4.1 , nécessitant deux constantes vectorielles.
    • AArch64 gcc: inlines à un seul instruction (prise en charge par HW pour ce mode d'arrondi ainsi que par défaut IEEE et la plupart des autres.)
  • std::floor / std::ceil / std::trunc

    • x86 clang: inlines à un seul insn avec -msse4.1
    • x86 gcc7.x: inlines à un seul insn avec -msse4.1
    • x86 gcc6.x et avant: inlines à un seul insn avec -ffast-math -msse4.1
    • AArch64 gcc: inlines par défaut à une seule instruction

arrondissements à int / long / long long :

vous avez deux options ici: utiliser lrint (comme rint mais retourne long , ou long long pour llrint ), ou utiliser une fonction D'arrondissement FP->FP et puis convertir en un type entier la normale façon (avec troncature). Certains compilateurs optimisent une manière mieux que l'autre.

long l = lrint(x);

int  i = (int)rint(x);

Note que int i = lrint(x) convertit float ou double -> long d'abord, et ensuite tronque la valeur de l'entier à int . Cela fait une différence pour les entiers hors de portée: comportement non défini en C++, mais bien défini pour les instructions x86 FP -> int (que le compilateur émettra à moins qu'il ne voit L'UB au moment de la compilation tout en faisant une propagation constante, puis il est permis de faire du code qui casse si jamais il est exécuté).

sur x86, une conversion FP->entière qui déborde l'entier produit INT_MIN ou LLONG_MIN (un motif bit de 0x8000000 ou l'équivalent 64 bits, avec juste le signe-bit mis). Intel appelle cela la valeur" integer indefinite". (Voir la cvttsd2si entrée manuelle , l'instruction SSE2 qui convertit (avec troncature) double scalaire à entier signé. C'est disponible avec une destination à 32 ou 64 bits (en mode 64 bits seulement). Il y a aussi un cvtsd2si (convertir avec le mode d'arrondi actuel), ce que nous aimerions que le compilateur émette, mais malheureusement gcc et clang ne le feront pas sans -ffast-math .

aussi attention que FP à / de unsigned int / long est moins efficace sur x86 (sans AVX512). La Conversion en 32-bit non signé sur une machine 64-bit est assez bon marché; il suffit de convertir en 64-bit signé et tronqué. Mais sinon, il est significativement plus lent.

  • x86 clang avec/sans -ffast-math -msse4.1 : (int/long)rint inlines roundsd / cvttsd2si . (optimisation manquée à cvtsd2si ). lrint ne colle pas du tout.

  • x86 gcc6.x et plus tôt sans -ffast-math : ni l'une ni l'autre des lignes

  • x86 gcc7 sans -ffast-math : (int/long)rint rounds et converts séparément (avec un total de 2 instructions de SSE4.1 est activé, sinon avec un tas de code inlined for rint sans roundsd ). lrint n'est pas en ligne.
  • x86 gcc avec -ffast-math : tous les chemins en ligne à cvtsd2si (optimal) , pas besoin de SSE4.1.

  • AArch64 gcc6.3 sans -ffast-math : (int/long)rint inlines à 2 instructions. lrint n'est pas en ligne

  • AArch64 gcc6.3 avec -ffast-math : (int/long)rint se compile à un appel à lrint . lrint n'est pas en ligne. Cela peut être une optimisation manquée à moins que les deux instructions que nous obtenons sans -ffast-math soient très lentes.
2
répondu Peter Cordes 2017-11-17 10:08:00

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);
    }

À compiler propre, comprend des mathématiques".h" et "limites" sont nécessaires. La fonction fonctionne selon un schéma d'arrondissement suivant:

  • tour de 5.0 5.0
  • tour de 3,8 4,0
  • tour de 2,3 2,0
  • ronde de 1,5 à 2,0
  • ronde de 0,501 égale 1,0
  • ronde de 0,5 est 1,0
  • ronde de 0.499 est 0.0
  • ronde de 0,01 est 0,0
  • ronde de 0,0 est de 0,0
  • cycle de -0.01 est -0.0
  • ronde de -0.499 est -0.0
  • ronde de -0,5 est -0,0
  • ronde de -0.501 est -1.0
  • cycle de -1.5 est -1.0
  • tour de -2.3 est -2.0
  • cycle de -3,8 is -4,0
  • cycle de -5,0 is -5,0
2
répondu konik 2017-12-03 02:58:52

si vous avez besoin de compiler du code dans des environnements qui supportent la norme C++11, mais aussi de compiler ce même code dans des environnements qui ne le supportent pas, vous pouvez utiliser une macro de fonction pour choisir entre std::round() et une fonction personnalisée pour chaque système. Il suffit de passer -DCPP11 ou /DCPP11 au compilateur compatible C++11 (ou d'utiliser ses macros de version intégrées), et de faire 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 .

cela se rapproche de std::round() dans les environnements qui ne sont pas conformes à C++11, y compris la conservation du bit de signe pour -0.0. Il peut toutefois causer un léger impact sur le rendement, et aura probablement des problèmes avec l'Arrondissement de certaines valeurs connues de "problème" à virgule flottante comme 0,499999999999994 ou des valeurs semblables.

alternativement, si vous avez accès à un compilateur compatible C++11, Vous pouvez il suffit de saisir std::round() de son en-tête <cmath> , et de l'utiliser pour créer votre propre en-tête qui définit la fonction si elle n'est pas déjà définie. Notez que cela peut ne pas être une solution optimale, cependant, surtout si vous avez besoin de compiler pour plusieurs plates-formes.

1
répondu Justin Time 2015-12-16 00:59:22

basé sur la réponse de Kalaxy, ce qui suit est une solution de modèle qui arrondit n'importe quel nombre de point flottant au type entier le plus proche basé sur l'arrondissement naturel. Il envoie également une erreur en mode debug si la valeur est hors de portée du type entier, servant ainsi grossièrement comme une 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);
    }
1
répondu quant 2016-08-31 00:08:04

comme indiqué dans les commentaires et autres réponses, la bibliothèque standard ISO c++ n'a pas ajouté round() jusqu'à ISO C++11, lorsque cette fonction a été retirée par référence à la bibliothèque standard de mathématiques ISO C99.

pour les opérandes positives dans [ ½ , ub ] round(x) == floor (x + 0.5) , où ub est 2 23 pour float lorsqu'il est cartographié à IEEE-754 (2008) binary32 , et 2 52 pour double quand elle est associée à la norme IEEE-754 (2008) binary64 . Les nombres 23 et 52 correspondent au nombre de bits stockés mantissa dans ces deux formats de virgule flottante. Pour les opérandes positives en [+0,½) round(x) == 0 , et pour les opérandes positives en ( ub , +∞] round(x) == x . Comme la fonction est symétrique par rapport à l'axe des x, les arguments négatifs x peuvent être traités selon round(-x) == -round(x) .

cela conduit à compact, le code ci-dessous. Il compile un nombre raisonnable d'instructions machine sur différentes plateformes. J'ai observé le code le plus compact sur GPUs, où my_roundf() nécessite environ une douzaine d'instructions. Selon l'architecture du processeur et la chaîne d'Outils, cette approche en virgule flottante pourrait être plus rapide ou plus lente que l'implémentation en entier de newlib référencée dans une réponse différente .

j'ai testé my_roundf() de manière exhaustive contre la nouvelle version du compilateur Intel roundf() , avec à la fois /fp:strict et /fp:fast . J'ai aussi vérifié que la version newlib correspond au roundf() de la bibliothèque mathimf du compilateur Intel. Des tests exhaustifs ne sont pas possibles pour la double précision round() , cependant le code est structurellement identique à la mise en œuvre de la 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;
}
1
répondu njuffa 2017-11-17 04:23:04

j'utilise l'implémentation suivante de round in asm pour l'architecture x86 et MS VS c++ spécifique:

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

UPD: pour retourner la valeur double

__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
0
répondu Aleksey F. 2015-01-04 15:19:01
// 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 mauvais moyen de conversion, mais bon sang, ça marche lol. Et c'est bien, parce que ça s'applique au vrai char. Non seulement affecter la sortie visuellement.

-4
répondu Brad 2017-12-03 02:58:01

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;
    }
}
-6
répondu Peter Mortensen 2017-12-03 02:56:36