¿Qué significa "% no está disponible: utilice truncatingRemainder en su lugar"?


104

Recibo el siguiente error cuando uso el código para una extensión, no estoy seguro de si me están pidiendo simplemente usar un operador diferente o modificar los valores en la expresión según una búsqueda en Internet.

Error:% no está disponible: utilice truncatingRemainder en su lugar

Código de extensión:

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

Los errores se producen al configurar las variables de minutos y segundos.


1
Creo que CMTimeGetSeconds devuelve float
zombie

3
Significa que el %operador no está disponible y debería considerar usar algo como el truncatingRemaindermétodo en su lugar.
Matt

1
no se puede utilizar módulo activado Float64sino activado Intúnicamente; por lo tanto: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60es la forma correcta.
holex

Respuestas:


174

CMTimeGetSeconds()devuelve un número de punto flotante ( Float64también conocido como Double). En Swift 2, podría calcular el resto de una división de punto flotante como

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

En Swift 3 esto se hace con

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

Aplicado a su código:

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

Sin embargo, en este caso particular, es más fácil convertir la duración a un número entero en primer lugar:

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

Luego, las siguientes líneas se simplifican a

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

24

El %operador de módulo se define solo para tipos enteros. Para los tipos de punto flotante, debe ser más específico sobre el tipo de comportamiento de división / resto IEEE 754 que desea, por lo que debe llamar a un método: remaindero truncatingRemainder. (Si está haciendo matemáticas de punto flotante, realmente debe preocuparse por esto y muchas otras cosas , o puede obtener resultados inesperados / malos).

Si realmente tiene la intención de hacer un módulo entero, debe convertir el valor de retorno de CMTimeGetSecondsen un número entero antes de usarlo %. (Tenga en cuenta que si lo hace, reducirá las fracciones de segundo ... dependiendo de dónde esté usando, CMTimeeso puede ser importante. ¿Quiere minutos: segundos: fotogramas, por ejemplo?)

Dependiendo de cómo desee presentar los CMTimevalores en su interfaz de usuario, podría ser mejor extraer el valor de los segundos y pasarlo NSDateFormattero NSDateComponentsFormatterpara obtener el soporte de configuración regional adecuado.


10

Recupere la sintaxis de módulo simple en swift 3:

Esta sintaxis fue sugerida en la lista de correo rápida oficial de Apples aquí, pero por alguna razón optaron por una sintaxis menos 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)
}

Este sencillo consejo de migración de Swift 3 es parte de una guía de migración de Swift 3 más completa con muchos conocimientos (35.000 ubicaciones / 8 días de migración) http://eon.codes/blog/2017/01/12/swift-3-migration /


1
Esta A está bien, proporciona información interesante y también intenta responder a la Q.
Jakub Truhlář

3
@Jakub Truhlář ... Hombre, Thx. En mi opinión, esta fue mi mejor solución de migración rápida 3. No puedo creer que la gente lo haya votado en contra. El módulo es un concepto tan importante y está pensado en todos los libros de códigos que tienen aritmética. Hacerlo detallado no tiene sentido ya que la aritmética en el código debe escribirse de la manera más compacta posible. A medida que nuestra capacidad cognitiva para comprender la aritmética aumenta cuando puede ver la imagen completa, se opone a comprender lo que significan las variables individuales. OMI La denominación integral de variables es importante en la lógica empresarial, pero no en la aritmética, todo lo contrario.
eonista

2
@GitSync Modulo es un concepto importante pero existe solo para enteros. Debes comprender la diferencia.
Sulthan

5
@GitSync La operación de módulo existe solo para enteros. Estás hablando de resto. Los valores decimales tienen dos tipos de residuos. Por eso Swift decidió hacer explícita la operación. No es muy común calcular un resto entero ( truncar el resto ) en valores dobles.
Sulthan

1
@GitSync Hay remainder(dividingBy:)y truncatingRemainder(dividingBy:). Es posible que desee leer los documentos de ambos. Además, consulte la misma pregunta para C ++ stackoverflow.com/questions/6102948/…
Sulthan

2

Descubrí que lo siguiente funciona en Swift 3:

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

donde totalSecondses un TimeInterval( Double).


3
Mezclar piso y ronda no es una buena idea, por ejemplo, totalSeconds = 59.8su código calcula 0 minutos y 0 segundos.
Martin R

Sí tienes razón. De hecho, roundno es necesario en absoluto.
benwiggy

1

No es necesario crear un operador de módulo separado para números de punto flotante, a menos que crea que hace que el código sea más seguro. Puede sobrecargar el %operador para aceptar números de punto flotante como este:

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

Uso

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

Ahora puede usar %con dos números de punto flotante del mismo tye.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.