Que signifie «% est indisponible: utilisez plutôt truncatingRemainder»?

105

J'obtiens l'erreur suivante lors de l'utilisation du code pour une extension, je ne suis pas sûr s'ils demandent simplement d'utiliser un opérateur différent ou de modifier les valeurs dans l'expression en fonction d'une recherche sur Internet.

Erreur:% n'est pas disponible: utilisez plutôt truncatingRemainder

Code d'extension:

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

Les erreurs se produisent lors de la définition des variables des minutes et des secondes.

Cosmic Arrows LLC
la source
1
Je pense que CMTimeGetSeconds renvoie float
zombie
3
Cela signifie que l' %opérateur n'est pas disponible et que vous devriez envisager d'utiliser quelque chose comme la truncatingRemainderméthode à la place.
mat
1
vous ne pouvez pas utiliser modulo sur Float64mais Intuniquement sur ; donc: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60est la bonne manière.
holex

Réponses:

174

CMTimeGetSeconds()renvoie un nombre à virgule flottante ( Float64aka Double). Dans Swift 2, vous pouvez calculer le reste d'une division en virgule flottante comme

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

Dans Swift 3, cela se fait avec

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

Appliqué à votre code:

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

Cependant, dans ce cas particulier, il est plus facile de convertir la durée en un entier en premier lieu:

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

Ensuite, les lignes suivantes se simplifient en

let hours = totalSeconds / 3600
let minutes = (totalSeconds % 3600) / 60
let seconds = totalSeconds % 60
Martin R
la source
24

L' %opérateur de module est défini uniquement pour les types entiers. Pour les types à virgule flottante, vous devez être plus précis sur le type de comportement de division / reste IEEE 754 souhaité, vous devez donc appeler une méthode: soit remainderou truncatingRemainder. (Si vous faites des calculs en virgule flottante, vous devez en fait vous soucier de cela, et de beaucoup d'autres choses , ou vous pouvez obtenir des résultats inattendus / mauvais.)

Si vous avez réellement l'intention de faire un module entier, vous devez convertir la valeur de retour de CMTimeGetSecondsen entier avant d'utiliser %. (Notez que si vous le faites, vous réduirez les fractions de seconde ... selon l'endroit où vous l'utilisez, CMTimecela peut être important. Voulez-vous minutes: secondes: images, par exemple?)

En fonction de la façon dont vous souhaitez présenter les CMTimevaleurs dans votre interface utilisateur, il peut être préférable d'extraire la valeur des secondes et de la transmettre à NSDateFormatterou NSDateComponentsFormatterafin d'obtenir la prise en charge des paramètres régionaux appropriés.

Rickster
la source
10

Ramenez la syntaxe modulo simple dans swift 3:

Cette syntaxe a en fait été suggérée sur la liste de diffusion officielle d'Apple ici, mais pour une raison quelconque, ils ont opté pour une syntaxe moins élégante.

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

Cette astuce de migration swift 3 simple fait partie d'un guide de migration swift 3 plus complet avec de nombreuses informations (35k loc / 8 jours de migration) http://eon.codes/blog/2017/01/12/swift-3-migration /

éoniste
la source
1
Ce A est très bien, fournit des informations intéressantes et tente également de répondre au Q.
Jakub Truhlář
3
@Jakub Truhlář ... Homme, Thx. IMO C'était mon meilleur correctif de migration Swift 3. Je ne peux pas croire que les gens ont voté à la baisse. Modulo est un concept si important et est pensé dans chaque livre de code qui a l'arithmétique. Le rendre verbeux n'a aucun sens puisque l'arithmétique dans le code doit être écrite de manière aussi compacte que possible. À mesure que notre capacité cognitive à comprendre l'arithmétique augmente, lorsque vous pouvez voir l'image complète comme opposée à la compréhension de la signification des variables individuelles. IMO La dénomination complète des variables est importante dans la logique métier mais pas en arithmétique, bien au contraire.
eonist
2
@GitSync Modulo est un concept important mais il n'existe que pour les entiers. Vous devez comprendre la différence.
Sulthan
5
@GitSync L'opération modulo n'existe que pour les entiers. Vous parlez de reste. Les valeurs décimales ont deux types de restes. C'est pourquoi Swift a décidé de rendre l'opération explicite. Il n'est pas très courant de calculer un reste entier ( reste tronqué ) sur des valeurs doubles.
Sulthan
1
@GitSync Il y a remainder(dividingBy:)et. truncatingRemainder(dividingBy:)Vous voudrez peut-être lire la documentation pour les deux. Aussi, reportez-vous à la même question pour C ++ stackoverflow.com/questions/6102948/…
Sulthan
2

J'ai trouvé que ce qui suit fonctionne dans Swift 3:

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

totalSecondsest un TimeInterval( Double).

Benwiggy
la source
3
Mélanger le sol et le rond n'est pas une bonne idée, par exemple, totalSeconds = 59.8votre code calcule 0 minute et 0 seconde.
Martin R
Oui tu as raison. En fait, ce roundn'est pas du tout nécessaire.
benwiggy
2

Il n'est pas nécessaire de créer un opérateur modulo séparé pour les nombres à virgule flottante, sauf si vous pensez que cela rend le code plus sûr. Vous pouvez surcharger l' %opérateur pour accepter les nombres à virgule flottante comme ceci:

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

Usage

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

Vous pouvez maintenant utiliser %avec deux nombres à virgule flottante du même tye.

Peter Schorn
la source