Apa artinya “% tidak tersedia: Gunakan truncatingRemainder sebagai gantinya”?


104

Saya mendapatkan kesalahan berikut saat menggunakan kode untuk ekstensi, saya tidak yakin apakah mereka meminta untuk hanya menggunakan operator yang berbeda atau mengubah nilai dalam ekspresi berdasarkan pencarian internet.

Kesalahan:% tidak tersedia: Gunakan truncatingRemainder sebagai gantinya

Kode ekstensi:

extension CMTime {
    var durationText:String {
        let totalSeconds = CMTimeGetSeconds(self)
        let hours:Int = Int(totalSeconds / 3600)
        let minutes:Int = Int(totalSeconds % 3600 / 60)
        let seconds:Int = Int(totalSeconds % 60)

        if hours > 0 {
            return String(format: "%i:%02i:%02i", hours, minutes, seconds)
        } else {
            return String(format: "%02i:%02i", minutes, seconds)
        }
    }
}

Kesalahan terjadi saat mengatur variabel menit dan detik.


1
saya pikir CMTimeGetSeconds mengembalikan float
zombie

3
Ini berarti %operator tidak tersedia dan Anda harus mempertimbangkan untuk menggunakan sesuatu seperti truncatingRemaindermetode ini.
matt

1
Anda tidak dapat menggunakan modulo on Float64tetapi Inthanya; oleh karena itu: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60adalah cara yang benar.
holex

Jawaban:


174

CMTimeGetSeconds()mengembalikan angka floating point ( Float64alias Double). Di Swift 2 Anda dapat menghitung sisa pembagian floating point sebagai

let rem = 2.5 % 1.1
print(rem) // 0.3

Di Swift 3 hal ini dilakukan dengan

let rem = 2.5.truncatingRemainder(dividingBy: 1.1)
print(rem) // 0.3

Diterapkan ke kode Anda:

let totalSeconds = CMTimeGetSeconds(self)
let hours = Int(totalSeconds / 3600)
let minutes = Int((totalSeconds.truncatingRemainder(dividingBy: 3600)) / 60)
let seconds = Int(totalSeconds.truncatingRemainder(dividingBy: 60))

Namun, dalam kasus khusus ini, lebih mudah untuk mengonversi durasi ke integer terlebih dahulu:

let totalSeconds = Int(CMTimeGetSeconds(self)) // Truncate to integer
// Or:
let totalSeconds = lrint(CMTimeGetSeconds(self)) // Round to nearest integer

Kemudian baris berikutnya disederhanakan menjadi

let hours = totalSeconds / 3600
let minutes = (totalSeconds % 3600) / 60
let seconds = totalSeconds % 60

24

The %Operator modulus didefinisikan hanya untuk tipe integer. Untuk tipe floating-point, Anda harus lebih spesifik tentang jenis perilaku pembagian / sisa IEEE 754 yang Anda inginkan, jadi Anda harus memanggil metode: salah satu remainderatau truncatingRemainder. (Jika Anda melakukan matematika floating-point, Anda benar-benar perlu peduli tentang ini, dan banyak hal lainnya , atau Anda bisa mendapatkan hasil yang tidak terduga / buruk.)

Jika Anda benar-benar bermaksud untuk melakukan modulus integer, Anda perlu mengonversi nilai pengembalian CMTimeGetSecondsmenjadi integer sebelum menggunakan %. (Perhatikan bahwa jika Anda melakukannya, Anda akan memotong detik pecahan ... tergantung di mana Anda menggunakan CMTimeyang mungkin penting. Apakah Anda ingin menit: detik: bingkai, misalnya?)

Bergantung pada bagaimana Anda ingin menyajikan CMTimenilai di UI Anda, mungkin lebih baik mengekstrak nilai detik dan meneruskannya ke NSDateFormatteratau NSDateComponentsFormatteragar Anda mendapatkan dukungan lokal yang sesuai.


10

Kembalikan sintaks modulo sederhana dengan cepat 3:

Sintaks ini sebenarnya disarankan di milis Swift resmi Apple di sini, tetapi karena alasan tertentu mereka memilih sintaks yang kurang elegan.

infix operator %%/*<--infix operator is required for custom infix char combos*/
/**
 * Brings back simple modulo syntax (was removed in swift 3)
 * Calculates the remainder of expression1 divided by expression2
 * The sign of the modulo result matches the sign of the dividend (the first number). For example, -4 % 3 and -4 % -3 both evaluate to -1
 * EXAMPLE: 
 * print(12 %% 5)    // 2
 * print(4.3 %% 2.1) // 0.0999999999999996
 * print(4 %% 4)     // 0
 * NOTE: The first print returns 2, rather than 12/5 or 2.4, because the modulo (%) operator returns only the remainder. The second trace returns 0.0999999999999996 instead of the expected 0.1 because of the limitations of floating-point accuracy in binary computing.
 * NOTE: Int's can still use single %
 * NOTE: there is also .remainder which supports returning negatives as oppose to truncatingRemainder (aka the old %) which returns only positive.
 */
public func %% (left:CGFloat, right:CGFloat) -> CGFloat {
    return left.truncatingRemainder(dividingBy: right)
}

Tip migrasi cepat 3 sederhana ini adalah bagian dari panduan migrasi cepat 3 yang lebih komprehensif dengan banyak wawasan (35k loc / 8-hari migrasi) http://eon.codes/blog/2017/01/12/swift-3-migration /


1
A ini baik-baik saja, memberikan informasi menarik dan juga mencoba menjawab Q.
Jakub Truhlář

3
@Jakub Truhlář ... Man, Thx. IMO Ini adalah perbaikan migrasi cepat 3 terbaik saya. Tidak percaya orang-orang memilihnya. Modulo adalah konsep yang penting dan dipikirkan di setiap buku kode yang memiliki aritmatika. Membuatnya bertele-tele tidak masuk akal karena Aritmatika dalam kode harus ditulis sekompak mungkin. Karena kemampuan kognitif kita untuk memahami aritmatika meningkat ketika Anda dapat melihat gambaran lengkap sebagai lawan untuk memahami apa arti variabel individu. Penamaan variabel IMO Comprehensive penting dalam logika bisnis tetapi bukan aritmatika, justru sebaliknya.
eonist

2
@GitSync Modulo adalah konsep penting tetapi hanya ada untuk bilangan bulat. Anda harus memahami perbedaannya.
Sulthan

5
@GitSync Operasi modulo hanya ada untuk bilangan bulat. Anda berbicara tentang sisa. Nilai desimal memiliki dua jenis sisa. Itu sebabnya Swift memutuskan untuk membuat operasi itu eksplisit. Tidaklah umum untuk menghitung sisa bilangan bulat ( sisa pemotongan ) pada nilai ganda.
Sulthan

1
@GitSync Ada remainder(dividingBy:)dan truncatingRemainder(dividingBy:). Anda mungkin ingin membaca dokumen untuk keduanya. Juga, lihat pertanyaan yang sama untuk C ++ stackoverflow.com/questions/6102948/…
Sulthan

2

Saya menemukan bahwa berikut ini berfungsi di Swift 3:

    let minutes = Int(floor(totalSeconds / 60))
    let seconds = Int(totalSeconds) % 60

di mana totalSecondsa TimeInterval( Double).


3
Mencampur lantai dan putaran bukanlah ide yang baik, misalnya untuk totalSeconds = 59.8kode Anda menghitung 0 menit dan 0 detik.
Martin R

Ya kau benar. Nyatanya, roundtidak dibutuhkan sama sekali.
benwiggy

1

Tidak perlu membuat operator modulo terpisah untuk bilangan floating point, kecuali menurut Anda itu membuat kode lebih aman. Anda dapat membebani %operator untuk menerima angka floating point seperti ini:

func %<N: BinaryFloatingPoint>(lhs: N, rhs: N) -> N {
    lhs.truncatingRemainder(dividingBy: rhs)
}

Pemakaian

let a: Float80 = 10
let b: Float80 = 3
print(a % b)

Sekarang Anda dapat menggunakan %dengan dua bilangan floating point dari tye yang sama.

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.