Comment arrondir un Double à l'Int le plus proche en Swift?

170

J'essaie de faire un calculateur du taux de croissance ( Double) qui arrondira le résultat à l'entier le plus proche et recalculera à partir de là, comme tel:

let firstUsers = 10.0
let growth = 0.1
var users = firstUsers
var week = 0


while users < 14 {
    println("week \(week) has \(users) users")
    users += users * growth
    week += 1
}

mais je n'ai pas pu jusqu'ici.

EDIT Je l'ai fait un peu comme ça:

var firstUsers = 10.0
let growth = 0.1
var users:Int = Int(firstUsers)
var week = 0


while users <= 14 {
    println("week \(week) has \(users) users")
    firstUsers += firstUsers * growth
    users = Int(firstUsers)
    week += 1
}

Bien que cela ne me dérange pas qu'il soit toujours arrondi, je n'aime pas cela car il firstUsersa dû devenir une variable et changer tout au long du programme (afin de faire le prochain calcul), ce que je ne veux pas que cela se produise.

duarte harris
la source

Réponses:

253

Il y a un rounddisponible dans la Foundationbibliothèque (il est en fait dans Darwin, mais Foundationimporte Darwinet la plupart du temps, vous voudrez l'utiliser Foundationau lieu d'utiliser Darwindirectement) .

import Foundation

users = round(users)

Exécuter votre code dans un terrain de jeu puis appeler:

print(round(users))

Les sorties:

15,0

round()arrondit toujours vers le haut lorsque la décimale est >= .5et vers le bas lorsqu'elle est < .5(arrondi standard). Vous pouvez utiliser floor()pour forcer l'arrondissement vers le bas et ceil()pour forcer l'arrondi vers le haut.

Si vous devez arrondir à un endroit spécifique, multipliez par pow(10.0, number of places), roundpuis divisez par pow(10, number of places):

Arrondir à 2 décimales:

let numberOfPlaces = 2.0
let multiplier = pow(10.0, numberOfPlaces)
let num = 10.12345
let rounded = round(num * multiplier) / multiplier
print(rounded)

Les sorties:

10.12

Remarque: en raison du fonctionnement des mathématiques en virgule flottante, elles roundedne sont pas toujours parfaitement précises. Il vaut mieux y penser davantage comme une approximation de l'arrondi. Si vous faites cela à des fins d'affichage, il est préférable d'utiliser la mise en forme de chaîne pour formater le nombre plutôt que d'utiliser les mathématiques pour l'arrondir.

Mike S
la source
Hmm pow()malheureusement pas disponible dans une aire de jeux
MrBr
1
@MrBr, pow()est défini dans la bibliothèque Darwin, vous devez donc d' import Darwinabord (ou import Foundationou import Cocoaou import UIKit, qui finissent tous par importer Darwin en interne).
Mike S
54
Il y a aussi lround()qui renvoie un Int.
Martin R
1
" round()arrondit toujours vers le haut lorsque la décimale est> = .5 et vers le bas quand il est <.5 (arrondi standard)." Sauf quand ce n'est pas le cas. round(-16.5)renvoie -17, pas -16. Est-ce un bug?
Daniel T.
1
@DanielT. - pas un bug. Il est arrondi au nombre négatif supérieur le plus proche. Pensez-y de cette façon, +16,5 à +17 s'éloigne de 0,5 de plus de zéro. Cela signifie que -16,5 à -17 est également 0,5 plus loin de zéro. Ceil serait le contraire, +16,5 à +16 est 0,5 plus proche de zéro et -16,5 à -16 est également 0,5 plus proche de zéro
adougies
139

Pour arrondir un double à l'entier le plus proche, utilisez simplement round().

var x = 3.7
x.round() // x = 4.0

Si vous ne souhaitez pas modifier la valeur d'origine, utilisez rounded():

let x = 3.7
let y = x.rounded() // y = 4.0. x = 3.7

Comme on pouvait s'y attendre ( ou pas ), un nombre comme 3.5est arrondi vers le haut et un nombre comme -3.5est arrondi vers le bas. Si vous avez besoin d'un comportement d'arrondi différent de celui-ci, vous pouvez utiliser l'une des règles d'arrondi . Par exemple:

var x = 3.7
x.round(.towardZero) // 3.0

Si vous avez besoin d'un réel, lancez-le Intsimplement en un (mais seulement si vous êtes certain que le Double ne sera pas supérieur à Int.max):

let myInt = Int(myDouble.rounded())

Remarques

  • Cette réponse est complètement réécrite. Ma vieille réponse traitée avec les fonctions mathématiques C aiment round, lround, flooret ceil. Cependant, maintenant que Swift a cette fonctionnalité intégrée, je ne peux plus recommander d'utiliser ces fonctions. Merci à @dfri de me l'avoir signalé. Découvrez l'excellente réponse de @ dfri ici . J'ai également fait quelque chose de similaire pour arrondir aCGFloat .
Suragch
la source
Int (myDouble.rounded ()) <--- cela pourrait en fait lever une exception si le double ne correspond pas à l'Int
Toad
@Toad, êtes-vous sûr? Je ne vois pas cela dans la documentation .
Suragch
Je viens de résoudre un crash en production avec ce problème précis. Mais même si j'avais tort et que cela ne planterait pas, cela donnerait quand même des résultats inattendus pour les doubles> maxint
Toad
1
@Toad, d'accord, bon point, merci. J'ai ajouté une note à la réponse.
Suragch
85

Swift 3 & 4 - utiliser la rounded(_:)méthode telle que définie dans le FloatingPointprotocole

Le FloatingPointprotocole (auquel par exemple Doubleet se Floatconforme) définit la rounded(_:)méthode

func rounded(_ rule: FloatingPointRoundingRule) -> Self

FloatingPointRoundingRuleest une énumération énumérant un certain nombre de règles d'arrondi différentes:

case awayFromZero

Arrondissez à la valeur autorisée la plus proche dont la magnitude est supérieure ou égale à celle de la source.

case down

Arrondissez à la valeur autorisée la plus proche inférieure ou égale à la source.

case toNearestOrAwayFromZero

Arrondir à la valeur autorisée la plus proche; si deux valeurs sont également proches, celle de plus grande amplitude est choisie.

case toNearestOrEven

Arrondir à la valeur autorisée la plus proche; si deux valeurs sont également proches, la paire est choisie.

case towardZero

Arrondissez à la valeur autorisée la plus proche dont la magnitude est inférieure ou égale à celle de la source.

case up

Arrondissez à la valeur autorisée la plus proche qui est supérieure ou égale à la source.

Nous utilisons des exemples similaires à ceux de l'excellente réponse de @ Suragch pour montrer ces différentes options d'arrondi dans la pratique.

.awayFromZero

Arrondir à la valeur autorisée la plus proche dont la magnitude est supérieure ou égale à celle de la source; pas d'équivalent direct parmi les fonctions C, car cela utilise, conditionnellement au signe de self, ceilou floor, pour des valeurs positives et négatives de self, respectivement.

3.000.rounded(.awayFromZero) // 3.0
3.001.rounded(.awayFromZero) // 4.0
3.999.rounded(.awayFromZero) // 4.0

(-3.000).rounded(.awayFromZero) // -3.0
(-3.001).rounded(.awayFromZero) // -4.0
(-3.999).rounded(.awayFromZero) // -4.0

.down

Équivalent à la floorfonction C.

3.000.rounded(.down) // 3.0
3.001.rounded(.down) // 3.0
3.999.rounded(.down) // 3.0

(-3.000).rounded(.down) // -3.0
(-3.001).rounded(.down) // -4.0
(-3.999).rounded(.down) // -4.0

.toNearestOrAwayFromZero

Équivalent à la roundfonction C.

3.000.rounded(.toNearestOrAwayFromZero) // 3.0
3.001.rounded(.toNearestOrAwayFromZero) // 3.0
3.499.rounded(.toNearestOrAwayFromZero) // 3.0
3.500.rounded(.toNearestOrAwayFromZero) // 4.0
3.999.rounded(.toNearestOrAwayFromZero) // 4.0

(-3.000).rounded(.toNearestOrAwayFromZero) // -3.0
(-3.001).rounded(.toNearestOrAwayFromZero) // -3.0
(-3.499).rounded(.toNearestOrAwayFromZero) // -3.0
(-3.500).rounded(.toNearestOrAwayFromZero) // -4.0
(-3.999).rounded(.toNearestOrAwayFromZero) // -4.0

Cette règle d'arrondi est également accessible en utilisant la rounded()méthode d' argument zéro .

3.000.rounded() // 3.0
// ...

(-3.000).rounded() // -3.0
// ...

.toNearestOrEven

Arrondir à la valeur autorisée la plus proche; si deux valeurs sont également proches, la paire est choisie; équivalent à la fonction C rint(/ très similaire à nearbyint).

3.499.rounded(.toNearestOrEven) // 3.0
3.500.rounded(.toNearestOrEven) // 4.0 (up to even)
3.501.rounded(.toNearestOrEven) // 4.0

4.499.rounded(.toNearestOrEven) // 4.0
4.500.rounded(.toNearestOrEven) // 4.0 (down to even)
4.501.rounded(.toNearestOrEven) // 5.0 (up to nearest)

.towardZero

Équivalent à la truncfonction C.

3.000.rounded(.towardZero) // 3.0
3.001.rounded(.towardZero) // 3.0
3.999.rounded(.towardZero) // 3.0

(-3.000).rounded(.towardZero) // 3.0
(-3.001).rounded(.towardZero) // 3.0
(-3.999).rounded(.towardZero) // 3.0

Si le but de l'arrondi est de se préparer à travailler avec un entier (par exemple en utilisant Intpar FloatPointinitialisation après arrondi), nous pourrions simplement utiliser le fait que lors de l'initialisation d'un en Intutilisant a Double(ou Floatetc), la partie décimale sera tronquée.

Int(3.000) // 3
Int(3.001) // 3
Int(3.999) // 3

Int(-3.000) // -3
Int(-3.001) // -3
Int(-3.999) // -3

.up

Équivalent à la ceilfonction C.

3.000.rounded(.up) // 3.0
3.001.rounded(.up) // 4.0
3.999.rounded(.up) // 4.0

(-3.000).rounded(.up) // 3.0
(-3.001).rounded(.up) // 3.0
(-3.999).rounded(.up) // 3.0

Addendum: visite du code source pour FloatingPointvérifier l'équivalence des fonctions C aux différentes FloatingPointRoundingRulerègles

Si nous le souhaitons, nous pouvons jeter un œil au code source du FloatingPointprotocole pour voir directement les équivalents de fonction C des FloatingPointRoundingRulerègles publiques .

Depuis swift / stdlib / public / core / FloatingPoint.swift.gyb, nous voyons que l'implémentation par défaut de la rounded(_:)méthode nous fait de la round(_:)méthode de mutation :

public func rounded(_ rule: FloatingPointRoundingRule) -> Self {
    var lhs = self
    lhs.round(rule)
    return lhs
}

Depuis swift / stdlib / public / core / FloatingPointTypes.swift.gyb, nous trouvons l'implémentation par défaut de round(_:), dans laquelle l'équivalence entre les FloatingPointRoundingRulerègles et les fonctions d'arrondi C est apparente:

public mutating func round(_ rule: FloatingPointRoundingRule) {
    switch rule {
    case .toNearestOrAwayFromZero:
        _value = Builtin.int_round_FPIEEE${bits}(_value)
    case .toNearestOrEven:
        _value = Builtin.int_rint_FPIEEE${bits}(_value)
    case .towardZero:
        _value = Builtin.int_trunc_FPIEEE${bits}(_value)
    case .awayFromZero:
        if sign == .minus {
            _value = Builtin.int_floor_FPIEEE${bits}(_value)
        }
        else {
            _value = Builtin.int_ceil_FPIEEE${bits}(_value)
        }
    case .up:
        _value = Builtin.int_ceil_FPIEEE${bits}(_value)
    case .down:
        _value = Builtin.int_floor_FPIEEE${bits}(_value)
    }
}
dfri
la source
@iosMentalist merci pour l'invite, j'ai mis à jour le titre de la réponse.
dfri
Si je veux une équation comme, 3,0 = 3, 3,1 = 3,5, 3,4 = 3,5, 3,6 = 4, 3,9 - 4
PJR
6
**In Swift**

var a = 14.123456789
var b = 14.123456789
var c = 14.123456789
var d = 14.123456789
var e = 14.123456789
var f = 14.123456789

a.rounded(.up)                      //15
b.rounded(.down)                    //14
c.rounded(.awayFromZero)            //15
d.rounded(.towardZero)              //14
e.rounded(.toNearestOrAwayFromZero) //14
f.rounded(.toNearestOrEven)         //14
Sai kumar Reddy
la source
6

Swift 3: Si vous voulez arrondir à un certain nombre de chiffres, par exemple 5,678434 -> 5,68, vous pouvez simplement combiner la fonction round () ou roundf () avec une multiplication:

let value:Float = 5.678434
let roundedValue = roundf(value * 100) / 100
print(roundedValue) //5.68
Thoms
la source
4

Vous pouvez également étendre FloatingPoint dans Swift 3 comme suit:

extension FloatingPoint {
    func rounded(to n: Int) -> Self {
        let n = Self(n)
        return (self / n).rounded() * n

    }
}

324.0.rounded(to: 5)   // 325
Leo Dabus
la source
Pouvez-vous expliquer cela? Que Selfsignifie?
JZAU
@Jacky Self fait référence à la classe FloatingPoint tandis que self fait référence à l'instance de cette classe.
George Yacoub
@GeorgeYacoub Self fait référence au type qui se conforme à FloatingPoint qui est étendu (dans cet exemple d'utilisation est un Double) mais ce sont des structures, pas des classes
Leo Dabus
2

Swift 3

var myNum = 8.09
myNum.rounded() // result = 8 and leaves myNum unmodified
Dattatray Deokar
la source
Agréable. Je ne connaissais pas celui-ci avant. Une remarque: myNum.rounded()ne change pas myNum, mais myNum.round()change.
Suragch
@Suragch, j'ai modifié la réponse pour refléter votre commentaire.
Adil Hussain le
0

Vous pouvez également vérifier si le double est supérieur à la valeur Int max avant d'essayer de convertir la valeur en Int.

let number = Double.infinity
if number >= Double(integerLiteral: Int64.max) {
  let rounded = Int.max
} else {
  let rounded = Int(number.rounded())
}
rockdaswift
la source
-1

Une solution très simple a fonctionné pour moi:

  if (62 % 50 != 0) {
      var number = 62 / 50 + 1 // adding 1 is doing the actual "round up"
  }

nombre contient la valeur 2

Nazar Medeiros
la source