Bagaimana cara membulatkan Double ke Int terdekat dengan cepat?

170

Saya mencoba membuat kalkulator tingkat pertumbuhan ( Double) yang akan membulatkan hasil ke Integer terdekat dan menghitung ulang dari sana, dengan demikian:

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
}

tapi saya belum bisa sejauh ini.

EDIT saya agak melakukannya seperti itu:

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
}

Walaupun saya tidak keberatan bahwa itu selalu dibulatkan, saya tidak suka karena firstUsersharus menjadi variabel dan berubah sepanjang program (untuk membuat perhitungan selanjutnya), yang saya tidak ingin itu terjadi.

duarte harris
sumber

Jawaban:

253

Ada yang roundtersedia di Foundationperpustakaan (sebenarnya ada di Darwin, tetapi Foundationimpor Darwindan sebagian besar waktu Anda akan ingin menggunakan Foundationdaripada menggunakan Darwinlangsung) .

import Foundation

users = round(users)

Menjalankan kode Anda di taman bermain dan kemudian menelepon:

print(round(users))

Output:

15.0

round()selalu dibulatkan ke atas ketika tempat desimal berada >= .5dan turun saat itu < .5(pembulatan standar). Anda dapat menggunakan floor()untuk memaksa membulatkan ke bawah, dan ceil()untuk memaksa membulatkan ke atas.

Jika Anda perlu untuk putaran ke tempat tertentu, maka Anda kalikan dengan pow(10.0, number of places), rounddan kemudian bagi dengan pow(10, number of places):

Membulatkan ke 2 tempat desimal:

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

Output:

10.12

Catatan: Karena cara kerja floating point matematika, roundedmungkin tidak selalu akurat sepenuhnya. Lebih baik memikirkannya lebih dari perkiraan pembulatan. Jika Anda melakukan ini untuk tujuan tampilan, lebih baik menggunakan pemformatan string untuk memformat angka daripada menggunakan matematika untuk membulatkannya.

Mike S
sumber
Hmm pow()sayangnya tidak tersedia di taman bermain
MrBr
1
@MrBr, pow()didefinisikan di perpustakaan Darwin, jadi Anda harus import Darwinterlebih dahulu ( import Foundationatau import Cocoaatau import UIKit, yang semuanya mengimpor Darwin secara internal).
Mike S
54
Ada juga lround()yang mengembalikan sebuah Int.
Martin R
1
" round()selalu dibulatkan ke atas ketika tempat desimal>> .5 dan turun ketika itu <.5 (pembulatan standar)." Kecuali kalau tidak. round(-16.5)mengembalikan -17, bukan -16. Apakah ini bug?
Daniel T.
1
@DanielT. - bukan bug. Ini membulatkan ke angka negatif terdekat yang lebih besar. Pikirkan seperti ini, +16.5 hingga +17 bergerak 0,5 lebih jauh dari nol. Itu berarti -16,5 ke -17 juga 0,5 lebih jauh dari nol. Ceil akan menjadi kebalikannya, +16.5 ke +16 adalah 0.5 lebih dekat ke nol dan -16.5 ke -16 juga 0.5 lebih dekat ke nol
adougies
139

Untuk membulatkan dobel ke bilangan bulat terdekat, cukup gunakan round().

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

Jika Anda tidak ingin mengubah nilai asli, gunakan rounded():

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

Seperti yang mungkin orang harapkan ( atau mungkin tidak ), angka seperti 3.5dibulatkan ke atas dan angka seperti -3.5dibulatkan ke bawah. Jika Anda membutuhkan perilaku pembulatan yang berbeda dari itu, Anda bisa menggunakan salah satu aturan pembulatan . Sebagai contoh:

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

Jika Anda membutuhkan aktual, Intmaka hanya melemparkannya ke satu (tetapi hanya jika Anda yakin bahwa Double tidak akan lebih besar dari Int.max):

let myInt = Int(myDouble.rounded())

Catatan

  • Jawaban ini sepenuhnya ditulis ulang. Jawaban lama saya ditangani dengan C fungsi matematika seperti round, lround, floor, dan ceil. Namun, sekarang karena Swift memiliki fungsi ini, saya tidak bisa lagi merekomendasikan untuk menggunakan fungsi-fungsi itu. Terima kasih kepada @dfri karena menunjukkan hal ini kepada saya. Lihat @ jawaban dfri di sini . Saya juga melakukan hal serupa untuk pembulatan aCGFloat .
Suragch
sumber
Int (myDouble.rounded ()) <--- ini mungkin benar-benar membuat pengecualian jika dobel tidak sesuai dengan Int
Toad
@ Memuat, Anda yakin? Saya tidak melihatnya di dokumentasi .
Suragch
Saya baru saja menyelesaikan kerusakan dalam produksi dengan masalah persis ini. Tetapi bahkan jika saya salah dan itu tidak akan crash, maka tetap saja itu akan tetap memberikan hasil yang tidak diharapkan untuk doubles> maxint
Toad
1
@ Memuat, benar, poin bagus, terima kasih. Saya menambahkan catatan pada jawabannya.
Suragch
85

Swift 3 & 4 - memanfaatkan rounded(_:)metode ini sebagai cetak biru dalam FloatingPointprotokol

The FloatingPointprotokol (yang misalnya Doubledan Floatsesuai) cetak biru yang rounded(_:)metode

func rounded(_ rule: FloatingPointRoundingRule) -> Self

Di mana FloatingPointRoundingRuleenum menyebutkan sejumlah aturan pembulatan yang berbeda:

case awayFromZero

Membulatkan ke nilai yang diizinkan terdekat yang besarnya lebih besar atau sama dengan nilai dari sumber.

case down

Membulatkan ke nilai yang diizinkan terdekat yang kurang dari atau sama dengan sumber.

case toNearestOrAwayFromZero

Membulatkan ke nilai yang diizinkan terdekat; jika dua nilai sama-sama dekat, yang dipilih dengan magnitudo lebih besar.

case toNearestOrEven

Membulatkan ke nilai yang diizinkan terdekat; jika dua nilai sama dekat, yang genap dipilih.

case towardZero

Membulatkan ke nilai yang diizinkan terdekat yang besarnya kurang dari atau sama dengan sumber.

case up

Membulatkan ke nilai yang diizinkan terdekat yang lebih besar dari atau sama dengan sumber.

Kami menggunakan contoh serupa dengan yang ada di @ Suragch sebagai jawaban terbaik untuk menunjukkan opsi pembulatan yang berbeda ini dalam praktik.

.awayFromZero

Membulatkan ke nilai yang diizinkan terdekat yang besarnya lebih besar atau sama dengan nilai dari sumber; tidak ada ekuivalen langsung di antara fungsi C, karena ini menggunakan, kondisional pada tanda self, ceilatau floor, untuk nilai positif dan negatif selfmasing-masing.

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

Setara dengan floorfungsi 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

Setara dengan roundfungsi 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

Aturan pembulatan ini juga dapat diakses menggunakan rounded()metode argumen nol .

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

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

.toNearestOrEven

Membulatkan ke nilai yang diizinkan terdekat; jika dua nilai sama dekat, yang genap dipilih; setara dengan fungsi C rint(/ sangat mirip dengan 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

Setara dengan truncfungsi 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

Jika tujuan pembulatan ini adalah untuk mempersiapkan untuk bekerja dengan integer (misalnya menggunakan Intoleh FloatPointinisialisasi setelah pembulatan), kita mungkin hanya memanfaatkan fakta bahwa ketika menginisialisasi Intmenggunakan Double(atau Floatdll), bagian desimal akan dipotong pergi.

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

Setara dengan ceilfungsi 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

Tambahan: mengunjungi kode sumber untuk FloatingPointmemverifikasi kesetaraan fungsi C dengan FloatingPointRoundingRuleaturan yang berbeda

Jika kita mau, kita dapat melihat kode sumber untuk FloatingPointprotokol untuk langsung melihat setara fungsi C dengan FloatingPointRoundingRuleaturan publik .

Dari swift / stdlib / public / core / FloatingPoint.swift.gyb kita melihat bahwa implementasi default rounded(_:)metode membuat kita dari round(_:)metode mutating :

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

Dari swift / stdlib / public / core / FloatingPointTypes.swift.gyb kami menemukan implementasi default round(_:), di mana kesetaraan antara FloatingPointRoundingRuleaturan dan fungsi pembulatan C tampak jelas:

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
sumber
@ iosMentalist terima kasih atas promptnya, saya telah memperbarui judul jawaban.
dfri
Jika saya ingin persamaan seperti, 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
sumber
6

Swift 3: Jika Anda ingin membulatkan ke angka digit tertentu mis. 5.678434 -> 5.68 Anda bisa menggabungkan fungsi round () atau roundf () dengan perkalian:

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

Anda juga dapat memperluas FloatingPoint di Swift 3 sebagai berikut:

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
sumber
Bisakah Anda jelaskan ini? Apa Selfartinya?
JZAU
@Jacky Self merujuk ke kelas FloatingPoint sedangkan self merujuk ke instance dari kelas itu.
George Yacoub
@GeorgeYacoub Self mengacu pada tipe yang sesuai dengan FloatingPoint yang sedang diperpanjang (dalam penggunaan sampel itu adalah Double) tetapi mereka adalah struktur, bukan kelas
Leo Dabus
2

Cepat 3

var myNum = 8.09
myNum.rounded() // result = 8 and leaves myNum unmodified
Dattatray Deokar
sumber
Bagus. Saya tidak tahu tentang ini sebelumnya. Satu catatan: myNum.rounded()tidak berubah myNum, tetapi myNum.round()tidak.
Suragch
@ Suragch, saya telah mengedit jawaban untuk mencerminkan komentar Anda.
Adil Hussain
0

Anda mungkin juga ingin memeriksa apakah dobel lebih tinggi dari nilai Int maks sebelum mencoba untuk mengubah nilai menjadi Int.

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

Solusi yang sangat mudah untuk saya:

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

angka berisi nilai 2

Medeiros Nazar
sumber