Como arredondar um Double para o Int mais próximo rapidamente?

170

Estou tentando fazer uma calculadora da taxa de crescimento ( Double) que arredondará o resultado para o número inteiro mais próximo e recalculará a partir daí, da seguinte forma:

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
}

mas eu tenho sido incapaz até agora.

EDIT Eu meio que fiz assim:

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
}

Embora eu não me importe que esteja sempre arredondando para baixo, eu não gosto disso porque firstUsersteve que se tornar uma variável e mudar ao longo do programa (para fazer o próximo cálculo), o que eu não quero que aconteça.

Duarte Harris
fonte

Respostas:

253

Existe um rounddisponível na Foundationbiblioteca (na verdade Darwin, ele está dentro , mas Foundationimporta Darwine na maioria das vezes você deseja usar em Foundationvez de usar Darwindiretamente) .

import Foundation

users = round(users)

Executando seu código em um playground e, em seguida, chamando:

print(round(users))

Saídas:

15,0

round()sempre arredonda para cima quando a casa decimal é >= .5e para baixo quando é < .5(arredondamento padrão). Você pode usar floor()para forçar o arredondamento para baixo e ceil()forçar o arredondamento para cima.

Se você precisa de volta para um lugar específico, então você multiplicar por pow(10.0, number of places), rounde depois dividir por pow(10, number of places):

Arredonde para duas casas decimais:

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

Saídas:

10,12

Nota: Devido à maneira como a matemática de ponto flutuante funciona, roundednem sempre é perfeitamente preciso. É melhor pensar nisso mais como uma aproximação do arredondamento. Se você estiver fazendo isso para fins de exibição, é melhor usar a formatação de string para formatar o número, em vez de usar matemática para arredondá-lo.

Mike S
fonte
Hmm pow(), infelizmente, não disponível em um playground
MRBR
1
@MrBr, pow()é definido na biblioteca do Darwin, então você precisa import Darwinprimeiro (ou import Foundationou import Cocoaou import UIKit, todos os quais acabam importando o Darwin internamente).
Mike S
54
Há também o lround()que retorna um Int.
Martin R
1
" round()sempre arredonda para cima quando a casa decimal é> = 0,5 e para baixo quando é <0,5 (arredondamento padrão)." Exceto quando não. round(-16.5)retorna -17, não -16. Isso é um inseto?
Daniel T.
1
@DanielT. - não é um bug. Está arredondando para o número negativo maior mais próximo. Pense dessa maneira: +16,5 a +17 está se afastando 0,5 do zero. Isso significa que -16,5 a -17 também está 0,5 a mais do zero. Ceil seria o oposto, 16,5-16 é 0,5 mais perto de zero e -16,5 a -16 também é 0,5 mais próximo de zero
adougies
139

Para arredondar um dobro para o número inteiro mais próximo, basta usar round().

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

Se você não deseja modificar o valor original, use rounded():

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

Como se poderia esperar ( ou não ), um número como 3.5é arredondado para cima e um número como -3.5é arredondado para baixo. Se você precisar de um comportamento de arredondamento diferente do que isso, poderá usar uma das regras de arredondamento . Por exemplo:

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

Se você precisar de um real Int, basta convertê-lo em um (mas apenas se tiver certeza de que o dobro não será maior que Int.max):

let myInt = Int(myDouble.rounded())

Notas

  • Esta resposta é completamente reescrita. Minha resposta velho lidou com o C funções matemáticas gostam round, lround, floor, e ceil. No entanto, agora que o Swift tem essa funcionalidade incorporada, não posso mais recomendar o uso dessas funções. Agradeço a @dfri por me indicar isso. Confira a excelente resposta da @ dfri aqui . Também fiz algo semelhante ao arredondar aCGFloat .
Suragch
fonte
Int (myDouble.rounded ()) <--- isso pode realmente lançar uma exceção se o casal não se encaixa na Int
Toad
@Toad, você tem certeza? Não vejo isso na documentação .
Suragch
Acabei de resolver uma falha na produção com esse problema exato. Mas mesmo se eu estava errado e que não iria falhar, então ainda que ainda daria resultados inesperados para duplas> maxint
Toad
1
@ Sapo, certo, bom ponto, obrigado. Eu adicionei uma nota à resposta.
Suragch
85

Swift 3 e 4 - fazendo uso do rounded(_:)método conforme o modelo do FloatingPointprotocolo

O FloatingPointprotocolo (com o qual, por exemplo, Doublee em Floatconformidade) define o rounded(_:)método

func rounded(_ rule: FloatingPointRoundingRule) -> Self

Onde FloatingPointRoundingRuleé uma enum enumerando várias regras de arredondamento diferentes:

case awayFromZero

Arredonde para o valor mais próximo permitido, cuja magnitude é maior ou igual à da fonte.

case down

Arredonde para o valor permitido mais próximo que seja menor ou igual à origem.

case toNearestOrAwayFromZero

Arredonde para o valor permitido mais próximo; se dois valores são igualmente próximos, aquele com maior magnitude é escolhido.

case toNearestOrEven

Arredonde para o valor permitido mais próximo; se dois valores são igualmente próximos, o mesmo é escolhido.

case towardZero

Arredonde para o valor mais próximo permitido, cuja magnitude é menor ou igual à da fonte.

case up

Arredonde para o valor permitido mais próximo que seja maior ou igual à origem.

Utilizamos exemplos semelhantes aos da excelente resposta do @ Suragch para mostrar essas diferentes opções de arredondamento na prática.

.awayFromZero

Arredonde para o valor mais próximo permitido, cuja magnitude é maior ou igual à da fonte; não há equivalente direto entre as funções C, pois isso usa condicionalmente o sinal de self, ceilou floor, para valores positivos e negativos de self, respectivamente.

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

Equivalente à floorfunção 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

Equivalente à roundfunção 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

Essa regra de arredondamento também pode ser acessada usando o rounded()método de argumento zero .

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

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

.toNearestOrEven

Arredonde para o valor permitido mais próximo; se dois valores são igualmente próximos, o mesmo é escolhido; equivalente à função C rint(/ muito semelhante a 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

Equivalente à truncfunção 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

Se o propósito do arredondamento é para se preparar para o trabalho com um inteiro (por exemplo, usando Intpela FloatPointinicialização após o arredondamento), poderíamos simplesmente fazer uso do fato de que, quando inicializar um Intusando um Double(ou Floatetc), a parte decimal será truncado distância.

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

Equivalente à ceilfunção 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

Adendo: visitando o código-fonte para FloatingPointverificar a equivalência das funções C às diferentes FloatingPointRoundingRuleregras

Se quisermos, podemos dar uma olhada no código fonte do FloatingPointprotocolo para ver diretamente a função C equivalente às FloatingPointRoundingRuleregras públicas .

Em swift / stdlib / public / core / FloatingPoint.swift.gyb , vemos que a implementação padrão do rounded(_:)método nos faz usar o método mutating round(_:):

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

Em swift / stdlib / public / core / FloatingPointTypes.swift.gyb , encontramos a implementação padrão de round(_:), na qual a equivalência entre as FloatingPointRoundingRuleregras e as funções de arredondamento C é aparente:

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
fonte
@iosMentalist obrigado pelo aviso, atualizei o título da resposta.
DFRI
Se quiser qualquer equação como, 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
fonte
6

Swift 3: Se você deseja arredondar para um determinado número de dígito, por exemplo, 5.678434 -> 5.68, basta combinar a função round () ou roundf () com uma multiplicação:

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

Você também pode estender o FloatingPoint no Swift 3 da seguinte maneira:

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
fonte
você pode explicar isso por favor? O que Selfsignifica?
JZAU
@Jacky Self refere-se à classe FloatingPoint, enquanto self refere-se à instância dessa classe.
George Yacoub
@GeorgeYacoub auto refere-se ao tipo que está em conformidade com ponto flutuante que está sendo estendida (em que o uso da amostra é um duplo) mas são estruturas, não classes
Leo Dabus
2

Swift 3

var myNum = 8.09
myNum.rounded() // result = 8 and leaves myNum unmodified
Dattatray Deokar
fonte
Agradável. Eu não sabia sobre isso antes. Uma observação: myNum.rounded()não muda myNum, mas muda myNum.round().
Suragch
@ Suragch, editei a resposta para refletir seu comentário.
Adil Hussain
0

Você também pode verificar se o dobro é maior que o valor máximo de Int antes de tentar converter o valor em um Int.

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

Uma solução muito fácil funcionou para mim:

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

número contém o valor 2

Nazar Medeiros
fonte