O que “% is unavailable: use truncatingRemainder em vez disso” significa?

104

Recebo o seguinte erro ao usar o código para uma extensão. Não tenho certeza se eles estão pedindo para usar apenas um operador diferente ou modificar os valores na expressão com base em uma pesquisa na Internet.

Erro:% não está disponível: use truncatingRemainder ao invés

Código de extensão:

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

O (s) erro (s) ocorrem ao definir as variáveis ​​de minutos e segundos.

Cosmic Arrows LLC
fonte
1
acho que CMTimeGetSeconds retorna float
zumbi
3
Isso significa que o %operador não está disponível e você deve considerar o uso de algo como o truncatingRemaindermétodo.
matt
1
você não pode usar o módulo ligado, Float64mas Intapenas ligado ; portanto: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60é a maneira correta.
holex

Respostas:

174

CMTimeGetSeconds()retorna um número de ponto flutuante ( Float64também conhecido como Double). No Swift 2, você pode calcular o resto de uma divisão de ponto flutuante como

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

No Swift 3, isso é feito com

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

Aplicado ao seu 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))

No entanto, neste caso específico, é mais fácil converter a duração em um número inteiro em primeiro lugar:

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

Então, as próximas linhas simplificam para

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

O %operador módulo é definido apenas para tipos inteiros. Para tipos de ponto flutuante, você precisa ser mais específico sobre o tipo de comportamento de divisão / resto IEEE 754 que deseja, portanto, é necessário chamar um método: remainderou truncatingRemainder. (Se estiver fazendo matemática de ponto flutuante, você realmente precisa se preocupar com isso e muitas outras coisas , ou você pode obter resultados inesperados / ruins.)

Se você realmente pretende fazer o módulo inteiro, é necessário converter o valor de retorno de CMTimeGetSecondspara um inteiro antes de usar %. (Observe que se você fizer isso, você cortará os segundos fracionários ... dependendo de onde você estiver usandoCMTime isso pode ser importante. Você quer minutos: segundos: quadros, por exemplo?)

Dependendo de como você deseja apresentar os CMTimevalores em sua IU, pode ser melhor extrair o valor de segundos e passá-lo para NSDateFormatterou NSDateComponentsFormatterpara obter o suporte de localidade apropriado.

rickster
fonte
10

Traga de volta a sintaxe do módulo simples no swift 3:

Esta sintaxe foi sugerida na lista de discussão oficial do Swift da Apples aqui , mas por algum motivo, eles optaram por uma sintaxe 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)
}

Esta dica simples de migração do Swift 3 é parte de um guia de migração do Swift 3 mais abrangente com muitos insights (35k loc / 8 dias de migração) http://eon.codes/blog/2017/01/12/swift-3-migration /

eonista
fonte
1
Este A é bom, fornece informações interessantes e também tenta responder a Q.
Jakub Truhlář
3
@Jakub Truhlář ... Cara, Thx. IMO Esta foi a minha melhor correção de migração do Swift 3. Não posso acreditar que as pessoas votaram contra. Módulo é um conceito tão importante e é pensado em todos os livros de código que possuem aritmética. Torná-lo detalhado não faz sentido, pois a aritmética no código deve ser escrita da forma mais compacta possível. À medida que nossa capacidade cognitiva de compreender a aritmética aumenta, você pode ver o quadro completo em oposição à compreensão do que significam as variáveis ​​individuais. A nomenclatura abrangente de variáveis ​​da IMO é importante na lógica de negócios, mas não na aritmética, muito pelo contrário.
eonista
2
Módulo @GitSync é um conceito importante, mas existe apenas para inteiros. Você deve entender a diferença.
Sulthan de
5
@GitSync A operação de módulo existe apenas para inteiros. Você está falando sobre resto. Os valores decimais têm dois tipos de resto. É por isso que Swift decidiu tornar a operação explícita. Não é muito comum calcular um resto inteiro ( resto truncado ) em valores duplos.
Sulthan
1
@GitSync Existe remainder(dividingBy:)e truncatingRemainder(dividingBy:). Você pode querer ler a documentação de ambos. Além disso, consulte a mesma pergunta para C ++ stackoverflow.com/questions/6102948/…
Sulthan
2

Descobri que o seguinte funciona no Swift 3:

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

onde totalSecondsestá um TimeInterval( Double).

benwiggy
fonte
3
Misturar chão e redondo não é uma boa ideia, por exemplo, porque totalSeconds = 59.8seu código calcula 0 minutos e 0 segundos.
Martin R
Sim, você está certo. Na verdade, roundnão é necessário.
benwiggy
1

Não há necessidade de criar um operador de módulo separado para números de ponto flutuante, a menos que você ache que isso torna o código mais seguro. Você pode sobrecarregar o %operador para aceitar números de ponto flutuante como:

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

Uso

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

Agora você pode usar %com quaisquer dois números de ponto flutuante do mesmo tipo.

Peter Schorn
fonte