Che cosa significa "% non disponibile: utilizza invece truncatingRemainder"?


104

Ottengo il seguente errore quando utilizzo il codice per un'estensione, non sono sicuro che stiano chiedendo semplicemente di utilizzare un operatore diverso o modificare i valori nell'espressione in base a una ricerca su Internet.

Errore:% non è disponibile: utilizza invece truncatingRemainder

Codice estensione:

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

Gli errori si verificano durante l'impostazione delle variabili dei minuti e dei secondi.


1
Penso che CMTimeGetSeconds restituisca float
zombie

3
Significa che l' %operatore non è disponibile e dovresti prendere in considerazione l'utilizzo di qualcosa di simile al truncatingRemaindermetodo.
matt

1
non puoi usare modulo su Float64ma Intsolo su ; quindi: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60è il modo corretto.
holex

Risposte:


174

CMTimeGetSeconds()restituisce un numero in virgola mobile ( Float64noto anche come Double). In Swift 2 puoi calcolare il resto di una divisione in virgola mobile come

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

In Swift 3 questo è fatto

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

Applicato al tuo codice:

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

Tuttavia, in questo caso particolare è più facile convertire la durata in un numero intero in primo luogo:

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

Quindi le righe successive si semplificano in

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

24

L' %operatore modulo è definito solo per i tipi interi. Per i tipi a virgola mobile, è necessario essere più specifici sul tipo di comportamento di divisione / resto IEEE 754 che si desidera, quindi è necessario chiamare un metodo: o remaindero truncatingRemainder. (Se stai facendo matematica in virgola mobile, in realtà devi preoccuparti di questo e di molte altre cose , altrimenti puoi ottenere risultati inaspettati / cattivi.)

Se si intende effettivamente eseguire il modulo intero, è necessario convertire il valore restituito di CMTimeGetSecondsin un numero intero prima dell'uso %. (Nota che se lo fai, taglierai le frazioni di secondo ... a seconda di dove stai usando CMTimeche potrebbe essere importante. Vuoi minuti: secondi: fotogrammi, per esempio?)

A seconda di come si desidera presentare i CMTimevalori nella propria interfaccia utente, potrebbe essere meglio estrarre il valore dei secondi e passarlo NSDateFormattero in NSDateComponentsFormattermodo da ottenere il supporto locale appropriato.


10

Riporta la semplice sintassi del modulo in swift 3:

Questa sintassi è stata effettivamente suggerita sulla mailing list ufficiale di Apple qui, ma per qualche motivo hanno optato per una sintassi meno elegante.

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

Questo semplice suggerimento per la migrazione di swift 3 fa parte di una guida alla migrazione di swift 3 più completa con molti approfondimenti (35k loc / 8 giorni di migrazione) http://eon.codes/blog/2017/01/12/swift-3-migration /


1
Questa A va bene, fornisce informazioni interessanti e cerca anche di rispondere alla Q.
Jakub Truhlář

3
@Jakub Truhlář ... Man, Thx. IMO Questa è stata la mia migliore soluzione per la migrazione a Swift 3. Non riesco a credere che la gente l'abbia votata. Modulo è un concetto così importante ed è pensato in ogni libro di codici che ha aritmetica. Renderlo dettagliato non ha senso poiché l'aritmetica nel codice dovrebbe essere scritta nel modo più compatto possibile. Man mano che la nostra capacità cognitiva di comprendere l'aritmetica aumenta quando puoi vedere il quadro completo rispetto alla comprensione del significato delle singole variabili. IMO La denominazione completa delle variabili è importante nella logica aziendale ma non nell'aritmetica, al contrario.
eonista

2
@GitSync Modulo è un concetto importante ma esiste solo per i numeri interi. Dovresti capire la differenza.
Sulthan

5
@GitSync L'operazione modulo esiste solo per i numeri interi. Stai parlando di resto. I valori decimali hanno due tipi di resto. Ecco perché Swift ha deciso di rendere esplicita l'operazione. Non è molto comune calcolare un resto intero ( troncamento del resto ) su valori doppi.
Sulthan

1
@ GitSync C'è remainder(dividingBy:)e truncatingRemainder(dividingBy:). Potresti voler leggere i documenti per entrambi. Inoltre, fare riferimento alla stessa domanda per C ++ stackoverflow.com/questions/6102948/…
Sulthan

2

Ho scoperto che quanto segue funziona in Swift 3:

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

dove totalSecondsè una TimeInterval( Double).


3
Mescolare floor e round non è una buona idea, ad esempio per il totalSeconds = 59.8tuo codice calcola 0 minuti e 0 secondi.
Martin R

Sì hai ragione. In effetti, roundnon è affatto necessario.
benwiggy

1

Non è necessario creare un operatore modulo separato per i numeri in virgola mobile, a meno che non si pensi che renda il codice più sicuro. Puoi sovraccaricare l' %operatore per accettare numeri in virgola mobile in questo modo:

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

uso

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

È ora possibile utilizzare %con due numeri in virgola mobile qualsiasi dello stesso tipo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.