Was bedeutet "% ist nicht verfügbar: Verwenden Sie stattdessen truncatingRemainder"?

103

Ich erhalte die folgende Fehlermeldung, wenn ich Code für eine Erweiterung verwende. Ich bin mir nicht sicher, ob sie nur einen anderen Operator verwenden oder die Werte im Ausdruck basierend auf einer Internetsuche ändern möchten.

Fehler:% ist nicht verfügbar: Verwenden Sie stattdessen truncatingRemainder

Erweiterungscode:

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

Die Fehler treten beim Einstellen der Minuten- und Sekundenvariablen auf.

Cosmic Arrows LLC
quelle
1
Ich denke, CMTimeGetSeconds gibt float
Zombie
3
Dies bedeutet, dass der %Operator nicht verfügbar ist und Sie truncatingRemainderstattdessen die Methode in Betracht ziehen sollten .
Matt
1
Sie können Modulo nicht verwenden, Float64sondern Intnur. deshalb: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60ist der richtige weg.
Holex

Antworten:

172

CMTimeGetSeconds()Gibt eine Gleitkommazahl ( Float64aka Double) zurück. In Swift 2 können Sie den Rest einer Gleitkommadivision als berechnen

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

In Swift 3 ist dies erledigt

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

Auf Ihren Code angewendet:

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

In diesem speziellen Fall ist es jedoch zunächst einfacher, die Dauer in eine Ganzzahl umzuwandeln:

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

Dann vereinfachen sich die nächsten Zeilen zu

let hours = totalSeconds / 3600
let minutes = (totalSeconds % 3600) / 60
let seconds = totalSeconds % 60
Martin R.
quelle
24

Der %Moduloperator ist nur für ganzzahlige Typen definiert. Bei Gleitkommatypen müssen Sie die Art des gewünschten IEEE 754-Teilungs- / Restverhaltens genauer festlegen, sodass Sie eine Methode aufrufen müssen: entweder remainderoder truncatingRemainder. (Wenn Sie Gleitkomma-Mathematik machen, müssen Sie sich tatsächlich darum kümmern und viele andere Dinge , sonst können Sie unerwartete / schlechte Ergebnisse erzielen.)

Wenn Sie tatsächlich einen Ganzzahlmodul verwenden möchten, müssen Sie den Rückgabewert von CMTimeGetSecondsvor der Verwendung in eine Ganzzahl konvertieren %. (Beachten Sie, CMTimedass Sie in diesem Fall die Sekundenbruchteile verlieren ... je nachdem, wo Sie sie verwenden , kann dies wichtig sein. Möchten Sie beispielsweise Minuten: Sekunden: Frames?)

Je nachdem , wie Sie wollen vorliegenden CMTimeWerte in Ihrer Benutzeroberfläche, könnte es besser sein , den Sekundenwert zu extrahieren und übergeben es an NSDateFormatteroder NSDateComponentsFormatterschauen Sie sich das jeweilige Land Unterstützung erhalten.

Rickster
quelle
10

Bringen Sie die einfache Modulo-Syntax in Swift 3 zurück:

Diese Syntax wurde tatsächlich auf der offiziellen schnellen Mailingliste von Apples hier vorgeschlagen, aber aus irgendeinem Grund entschieden sie sich für eine weniger elegante Syntax.

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

Dieser einfache Swift 3-Migrationstipp ist Teil eines umfassenderen Swift 3-Migrationsleitfadens mit vielen Erkenntnissen (35.000 Loc / 8 Tage Migration) http://eon.codes/blog/2017/01/12/swift-3-migration /.

Eonist
quelle
1
Dieses A ist in Ordnung, liefert interessante Informationen und versucht auch, das Q zu beantworten.
Jakub Truhlář
3
@ Jakub Truhlář ... Mann, Thx. IMO Dies war mein bester schneller 3-Migrations-Fix. Ich kann nicht glauben, dass die Leute es abgelehnt haben. Modulo ist ein so wichtiges Konzept und wird in jedem arithmetischen Codebuch berücksichtigt. Es macht keinen Sinn, es ausführlich zu machen, da die Arithmetik im Code so kompakt wie möglich geschrieben werden sollte. Wenn unsere kognitive Fähigkeit, Arithmetik zu verstehen, zunimmt, können Sie das gesamte Bild sehen, anstatt zu verstehen, was die einzelnen Variablen bedeuten. IMO Umfassende Variablennamen sind in der Geschäftslogik wichtig, aber nicht arithmetisch, ganz im Gegenteil.
Eonist
2
@GitSync Modulo ist ein wichtiges Konzept, das jedoch nur für Ganzzahlen existiert. Sie sollten den Unterschied verstehen.
Sulthan
5
@GitSync Die Modulo-Operation existiert nur für ganze Zahlen. Sie sprechen über den Rest. Dezimalwerte haben zwei Arten von Resten. Aus diesem Grund hat Swift beschlossen, die Operation explizit zu machen. Es ist nicht sehr üblich, einen ganzzahligen Rest ( abgeschnittenen Rest ) für doppelte Werte zu berechnen .
Sulthan
1
@GitSync Es gibt remainder(dividingBy:)und. truncatingRemainder(dividingBy:)Vielleicht möchten Sie die Dokumente für beide lesen. Beziehen Sie
Sulthan
2

Ich habe festgestellt, dass in Swift 3 Folgendes funktioniert:

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

wo totalSecondsist ein TimeInterval( Double).

benwiggy
quelle
3
Das Mischen von Boden und Runde ist keine gute Idee, z. B. für totalSeconds = 59.8Ihren Code werden 0 Minuten und 0 Sekunden berechnet.
Martin R
Ja, du hast Recht. Tatsächlich wird das überhaupt roundnicht benötigt.
Benwiggy
0

Es ist nicht erforderlich, einen separaten Modulo-Operator für Gleitkommazahlen zu erstellen, es sei denn, Sie glauben, dass dies den Code sicherer macht. Sie können den %Operator überladen , um Gleitkommazahlen wie folgt zu akzeptieren:

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

Verwendung

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

Sie können jetzt %mit zwei beliebigen Gleitkommazahlen desselben Tye verwenden.

Peter Schorn
quelle