web-dev-qa-db-fra.com

Arrondir un flotteur à une grille régulière de points prédéfinis

Je veux arrondir un nombre flottant à une précision donnée, par exemple:

0.051 i want to convert it to
0.1

0.049 i want to convert it to
0.0

0.56 i want to convert it to
0.6

0.54 i want to convert it to
0.5

Je ne peux pas l'expliquer mieux, mais la raison en est de traduire un emplacement de point (comme 0.131f, 0.432f) en un carreau dans une grille (comme 0.1f, 0.4f).

23
SteveL

Tant que votre grille est régulière, il vous suffit de trouver une transformation d’entiers en cette grille. Alors disons que votre grille est

0.2  0.4  0.6  ...

Puis vous tournez par

float round(float f)
{
    return floor(f * 5 + 0.5) / 5;
    // return std::round(f * 5) / 5; // C++11
}
24
marton78

Les fonctions standard ceil(), floor() n'ont pas de précision. Vous pouvez contourner ce problème en ajoutant votre propre précision - mais cela peut introduire des erreurs - par exemple.

double ceil(double v, int p)
{
  v *= pow(10, p);
  v = ceil(v);
  v /= pow(10, p);
}

Je suppose que vous pourriez tester pour voir si cela est fiable pour vous?

7
Nim

Un algorithme que vous pouvez utiliser:

  • obtenir 10 pour que le pouvoir (nombre de chiffres significatifs) (= P10)
  • multipliez votre double valeur par P10
  • ajouter: 0.5 (ou soustraire si négatif - voir le commentaire d'Ankush Shah)
  • divisez la partie entière de cette somme par (P10) - la réponse sera votre nombre arrondi
6
slashmais

EDIT 1: Je cherchais des solutions pour numpy en python et je ne savais pas que le PO demandait C++ haha, eh bien.

EDIT 2: Lol, on dirait que je n'ai même pas répondu à votre question initiale. Il semble que vous vouliez vraiment arrondir selon une décimale (l'opération est indépendante du nombre donné), pas une précision (l'opération dépend du nombre), les autres ont déjà répondu à cette question.

En fait, je cherchais également cela, mais je ne trouvais rien, alors j’ai assemblé une implémentation pour les tableaux numpy. On dirait qu'il met en œuvre la logique que slashmais a énoncée.

def pround(x, precision = 5):
    temp = array(x)
    ignore = (temp == 0.)
    use = logical_not(ignore)
    ex = floor(log10(abs(temp[use]))) - precision + 1
    div = 10**ex
    temp[use] = floor(temp[use] / div + 0.5) * div
    return temp

Voici une version scalaire C++ également, et vous pourriez probablement faire quelque chose de similaire à ci-dessus en utilisant Eigen (ils ont une indexation logique):

#include <cmath>
#include <iostream>
#include <vector>
#include <boost/foreach.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>

using namespace std;

double pround(double x, int precision)
{
    if (x == 0.)
        return x;
    int ex = floor(log10(abs(x))) - precision + 1;
    double div = pow(10, ex);
    return floor(x / div + 0.5) * div;
}

    template<typename T>
vector<T>& operator<<(vector<T> &x, const T &item)
{
    x.Push_back(item);
    return x;
}

int main()
{
    vector<double> list;
    list << 0.051 << 0.049 << 0.56 << 0.54;
    // What the OP was wanting
    BOOST_FOREACH(double x, list)
    {
        cout << floor(x * 10 + 0.5) / 10 << "\n";
    }

    cout << "\n";

    BOOST_FOREACH(double x, list)
    {
        cout << pround(x, 0) << "\n";
    }

    cout << "\n";

    boost::function<double(double)> rounder = boost::bind(&pround, _1, 3);
    vector<double> newList;
    newList << 1.2345 << 1034324.23 << 0.0092320985;
    BOOST_FOREACH(double x, newList)
    {
        cout << rounder(x) << "\n";
    }

    return 0;
}

Sortie:

0.1
0
0.6
0.5

0.1
0
1
1

1.23
1.03e+06
0.00923
5
eacousineau

Utilisez floor() et ceil(). floor convertira un float en entier plus petit, et ceil en supérieur:

floor( 4.5 ); // returns 4.0
ceil( 4.5 );  // returns 5.0

Je pense que ce qui suit fonctionnerait:

float round( float f )
{   
    return floor((f * 10 ) + 0.5) / 10;
}

floor( f + 0.5 ) arrondira à un entier. En multipliant d'abord par 10 puis en divisant le résultat par 10, vous arrondissez par incréments de 0,1.

4
Rafael Baptista

Habituellement, vous connaissez la précision souhaitée au moment de la compilation. Par conséquent, en utilisant la fonction Pow basée sur un modèle disponible ici , vous pouvez faire:

template <int PRECISION>
float roundP(float f)
{
    const int temp = Pow<10,PRECISION>::result;
    return roundf(f*temp)/temp;
}

int main () {
    std::cout << std::setprecision(10);
    std::cout << roundP<0>(M_PI) << std::endl;
    std::cout << roundP<1>(M_PI) << std::endl;
    std::cout << roundP<2>(M_PI) << std::endl;
    std::cout << roundP<3>(M_PI) << std::endl;
    std::cout << roundP<4>(M_PI) << std::endl;
    std::cout << roundP<5>(M_PI) << std::endl;
    std::cout << roundP<6>(M_PI) << std::endl;
    std::cout << roundP<7>(M_PI) << std::endl;
}

Testé ici .

Le résultat montre également combien la représentation en virgule flottante est imprécise :)

3

3.099999905

3.140000105

3.14199996

3.141599894

3.141590118

3.141592979

3.141592741

Vous pouvez avoir de meilleurs résultats en utilisant double:

template <int PRECISION>
double roundP(double f)
{
    const int temp = Pow<10,PRECISION>::result;
    return round(f*temp)/temp;
}

Imprimé avec précision 20:

3

3.1000000000000000888

3.1400000000000001243

3.1419999999999999041

3.14159999999999994848

3.1415899999999998826

3.1415929999999998579

3.1415926999999999047

2
Antonio

Je vais brièvement optimiser les dernières réponses en convertissant le nombre entré en un double premier pour éviter tout débordement. Un exemple de fonction (pas trop jolie, mais fonctionne très bien):

#include <cmath>

// round float to n decimals precision
float round_n (float num, int dec)
{
    double m = (num < 0.0) ? -1.0 : 1.0;   // check if input is negative
    double pwr = pow(10, dec);
    return float(floor((double)num * m * pwr + 0.5) / pwr) * m;
}
1
Alexander

Comme Mooing Duck a modifié ma question et supprimé le code en disant que les questions ne devraient pas contenir de réponses (compréhensibles), je vais écrire la solution ici:

float round(float f,float prec)
{
    return (float) (floor(f*(1.0f/prec) + 0.5)/(1.0f/prec));
}
0
SteveL

Algorithme pour arrondir le nombre flottant:

 double Rounding(double src, int precision) {
         int_64 des;
         double tmp;
         double result;
         tmp = src * pow(10, precision);
         if(tmp < 0) {//negative double
            des = (int_64)(tmp - 0.5);
         }else {
            des = (int_64)(tmp + 0.5);
         }
         result = (double)((double)dst * pow(10, -precision));
         return result;
    }
0
KidNg

Vous pouvez arrondir un nombre à la précision souhaitée avec la fonction suivante

double round(long double number, int precision) {
  int decimals = std::pow(10, precision);
  return (std::round(number * decimals)) / decimals;
}

Vérifiez quelques exemples ci-dessous ...

1)

round(5.252, 0)
returns => 5

2)

round(5.252, 1)
returns => 5.3

3)

round(5.252, 2)
returns => 5.25

4)

round(5.252, 3)
returns => 5.252

Cette fonction fonctionne même pour les nombres avec une précision de 9.

5)

round(5.1234500015, 9)
returns => 5.123450002
0
Victor