web-dev-qa-db-fra.com

Comment plancher ou plafond en toute sécurité un CGFloat à int?

J'ai souvent besoin de plancher ou de plafond un CGFloat à un int, pour le calcul d'un index de tableau.

Le problème que je vois en permanence avec floorf(theCGFloat) ou ceilf(theCGFloat) est qu'il peut y avoir des problèmes avec des inexactitudes en virgule flottante.

Et si mon CGFloat est 2.0f mais en interne, il est représenté par 1.999999999999f ou quelque chose comme ça. Je fais floorf et je reçois 1.0f, qui est à nouveau un flotteur. Et pourtant je dois lancer cette bête en int qui peut introduire un autre problème.

Existe-t-il une meilleure pratique pour fixer le plancher ou le plafond d'un float à un int de telle sorte que quelque chose comme 2.0 ne serait jamais accidentellement condamné à 1 et quelque chose comme 2.0 ne serait jamais accidentellement plafonné à 2?

20
Proud Member

Il y a quelques idées fausses dans votre question.

que faire si mon CGFloat est 2.0f mais en interne il est représenté par 1.999999999999f

ne peut pas arriver; 2.0, comme tous les entiers raisonnablement petits, a une représentation exacte en virgule flottante. Si votre CGFloat est 2.0f, alors c'est vraiment 2.0.

quelque chose comme 2.0 ne serait jamais accidentellement plafonné à 2

Le plafond de 2,0 est 2; que serait-ce d'autre?


Je pense que la question que vous posez vraiment est "supposons que je fasse un calcul qui produit un résultat inexact, qui mathématiquement devrait soit exactement 2.0, mais est en fait légèrement moins; lorsque j'applique floor à cette valeur, j'obtiens 1.0 au lieu de 2.0 - comment puis-je éviter cela? "

C'est en fait une question assez subtile qui n'a pas une seule "bonne" réponse. Comment avez-vous calculé la valeur d'entrée? Qu'allez-vous faire du résultat?

20
Stephen Canon

Réponse supplémentaire rapide

J'ajoute cela comme une réponse supplémentaire pour ceux qui viennent ici en regardant comment utiliser floor et ceil avec un CGFloat (comme je l'ai fait).

var myCGFloat: CGFloat = 3.001
floor(myCGFloat) // 3.0
ceil(myCGFloat) // 4.0

Et si un Int est nécessaire, il peut être converti en un.

var myCGFloat: CGFloat = 3.001
Int(floor(myCGFloat)) // 3
Int(ceil(myCGFloat)) // 4

Mise à jour

Il n'est plus nécessaire d'utiliser les fonctions C floor et ceil. Vous pouvez utiliser le Swift round() avec règles d'arrondi .

var myCGFloat: CGFloat = 3.001
myCGFloat.round(.down) // 3.0
myCGFloat.round(.up) // 4.0

Si vous ne souhaitez pas modifier les variables d'origine, utilisez alors rounded().

Notes

  • Fonctionne avec les architectures 32 et 64 bits.

Voir aussi

25
Suragch

EDIT - lisez les commentaires pour les raisons pour lesquelles cette réponse n'est pas correcte :)

La conversion d'un float en int est un floorf implicite, c'est-à-dire que (int)5.9 Est 5. Si cela ne vous dérange pas, lancez simplement :)

Si vous voulez arrondir, il vous suffit de lancer le résultat d'un ceilf - un casting après l'arrondi ne devrait pas introduire d'erreurs du tout (ou, si vous le souhaitez, ajoutez-en un avant le cast, par exemple `(int) (5.9+ 1) 'est' 6 '- identique à l'arrondi).

Pour arrondir au plus proche, ajoutez simplement 0,5 - (int)(5.9+0.5) Est 6 Mais (int)(5.4+0.5) Est 5. Bien que j'utiliserais simplement roundf(5.9) :)

4
deanWombourne