2009-03-03 3 views
32

Je voudrais une fonction floor avec la syntaxeEst-ce que lancer à un int après std :: floor garantit le bon résultat?

int floor(double x); 

mais std::floor retourne un double. Est-ce

static_cast <int> (std::floor(x)); 

garantie pour me donner l'entier correct, ou que je pourrais avoir un problème hors par un? Cela semble fonctionner, mais j'aimerais bien le savoir.

Pour les points bonus, pourquoi le std::floor renvoie-t-il un double en premier lieu?

Répondre

25

La gamme de double est manière supérieure à la plage de 32 ou 64 nombres entiers binaires, ce qui explique pourquoi std::floor renvoie un double. Casting à int devrait être bien aussi longtemps que c'est dans la plage appropriée - mais sachez qu'un double ne peut pas représenter tous les entiers 64 bits exactement, de sorte que vous pouvez également vous retrouver avec des erreurs lorsque vous allez au-delà du point où la précision double est telle que la différence entre deux doubles consécutifs est supérieur à 1.

+0

donc, s'il est dans la bonne gamme, la distribution est très bien Où est-il dit (ce qui implique) cela dans la spécification? ? –

+0

Eh bien, il y a deux opérations ici: std :: floor, et la distribution std :: floor est destinée à renvoyer un entier, et la distribution est spécifiée en termes d'une valeur entière Tant que std :: floor retourne une valeur qui est vraiment un entier exact, je ca pas voir à quel point cela aurait du sens d'échouer. –

+0

Comment floor pourrait-il retourner quelque chose qui n'est pas un entier exact? Pour les petits doubles (epsilon << 1), toutes les valeurs entières peuvent être représentées, y compris floor (x). Pour les grands doubles (epsilon >> 1), seules les valeurs intégrales peuvent être représentées, donc floor (x) == x. – MSalters

12
static_cast <int> (std::floor(x)); 

fait à peu près ce que vous voulez, oui. Il vous donne l'entier le plus proche, arrondi vers -infinity. Au moins tant que votre entrée est dans la plage représentable par ints. Je ne suis pas sûr de ce que vous entendez par 'ajouter .5 et autres joyeusetés, mais il n'aura pas le même effet

Et std :: floor renvoie un double parce que c'est le plus général. Parfois, vous pourriez vouloir arrondir un flotteur ou un double, mais conserver le type. C'est-à-dire, arrondir 1.3f à 1.0f, plutôt qu'à 1.

Ce serait difficile à faire si std :: floor renvoyait un int. (ou au moins vous auriez une distribution supplémentaire inutile ralentissant les choses).

Si floor n'effectue que l'arrondi lui-même, sans changer le type, vous pouvez le convertir en int si/quand vous en avez besoin.

Une autre raison est que la gamme de doubles est beaucoup plus grande que celle de ints. Il ne sera peut-être pas possible d'arrondir tous les doubles à ints.

+1

floor() tourne vers -infinity, pas zéro: Voir http://www.cplusplus.com/reference/clibrary/cmath/floor.html –

+0

doh, vous avez raison bien sûr. Fixé. – jalf

5

Si vous voulez gérer différentes conditions numériques et que vous souhaitez gérer différents types de conversions de manière contrôlée, vous devriez peut-être consulter le Boost.NumericConversion. Cette bibliothèque permet de traiter des cas étranges (comme hors de portée, arrondi, plages, etc.)

Voici l'exemple de la documentation:

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

int main() { 

    typedef boost::numeric::converter<int,double> Double2Int ; 

    int x = Double2Int::convert(2.0); 
    assert (x == 2); 

    int y = Double2Int()(3.14); // As a function object. 
    assert (y == 3) ; // The default rounding is trunc. 

    try 
    { 
     double m = boost::numeric::bounds<double>::highest(); 
     int z = Double2Int::convert(m); // By default throws positive_overflow() 
    } 
    catch (boost::numeric::positive_overflow const&) 
    { 
    } 

    return 0; 
} 
2

La plupart de la bibliothèque mathématique standard utilise double mais fournit également des versions flottantes. std :: floorf() est la version simple de std :: floor() si vous préférez ne pas utiliser les doubles.

Modifier: J'ai supprimé une partie de ma réponse précédente. J'avais déclaré que le sol était redondant lors de la conversion en int, mais j'ai oublié que cela n'est vrai que pour les nombres positifs en virgule flottante.

6

La norme C++ dit (4.9.1):

« An rvalue d'un type à virgule flottante peut être converti en un rvalue de type entier La conversion tronque, à savoir 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ée dans le type de destination "

Donc si vous convertissez un double en un entier, le nombre est dans la plage de int et l'arrondi requis est vers zéro, il suffit de simplement jeter le nombre d'int:

(int) x;

+0

La troncature de la partie fraction n'est pas floor - le résultat est différent pour les nombres négatifs. – Suma

+0

J'ai clairement écrit "Donc si ... l'arrondi requis est vers zéro" pour distinguer ce cas de "arrondi vers -fini". Le plancher est arrondi vers -infinity, troncature - vers zéro. –

Questions connexes