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.

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.


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 .

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)
    }
}

@ 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

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

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

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

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())
}

-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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.