Co oznacza „% jest niedostępny: zamiast tego użyj truncatingRemainder”?

105

Podczas używania kodu rozszerzenia pojawia się następujący błąd: nie jestem pewien, czy proszą o użycie innego operatora lub zmodyfikowanie wartości w wyrażeniu na podstawie wyszukiwania w Internecie.

Błąd:% jest niedostępny: zamiast tego użyj truncatingRemainder

Kod rozszerzenia:

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

Błędy występują podczas ustawiania zmiennych minut i sekund.

Cosmic Arrows LLC
źródło
1
Myślę, że CMTimeGetSeconds zwraca float
zombie
3
Oznacza to, że %operator jest niedostępny i powinieneś truncatingRemainderzamiast tego rozważyć użycie czegoś podobnego do metody.
mat
1
nie możesz używać modulo na, Float64ale Inttylko na; dlatego: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60jest właściwą drogą.
holex
@holex. Mylisz się. Operatora modulo można używać tylko na operandach o typach zgodnych z BinaryInteger, a nie tylko Int.
Peter Schorn
@PeterSchorn, dzięki za poprawienie komentarza sprzed 3 lat - ten protokół w ogóle nie był dostępny.
holex

Odpowiedzi:

174

CMTimeGetSeconds()zwraca liczbę zmiennoprzecinkową ( Float64aka Double). W Swift 2 można obliczyć pozostałą część dzielenia zmiennoprzecinkowego jako

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

W Swift 3 odbywa się to za pomocą

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

Zastosowano do twojego kodu:

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

Jednak w tym konkretnym przypadku łatwiej jest najpierw przekonwertować czas trwania na liczbę całkowitą:

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

Następnie następne wiersze upraszczają się do

let hours = totalSeconds / 3600
let minutes = (totalSeconds % 3600) / 60
let seconds = totalSeconds % 60
Martin R.
źródło
24

%Operatora moduł jest określony tylko przez liczby całkowite. W przypadku typów zmiennoprzecinkowych musisz bardziej szczegółowo określić rodzaj zachowania podziału / reszty IEEE 754, który chcesz, więc musisz wywołać metodę: albo remainderlub truncatingRemainder. (Jeśli wykonujesz obliczenia zmiennoprzecinkowe, musisz się tym zająć i wieloma innymi rzeczami , inaczej możesz uzyskać nieoczekiwane / złe wyniki).

Jeśli faktycznie zamierzasz uzyskać moduł CMTimeGetSecondscałkowity, przed użyciem musisz przekonwertować zwracaną wartość na liczbę całkowitą %. (Pamiętaj, że jeśli to zrobisz, odetniesz ułamkowe części sekund ... w zależności od tego, gdzie używasz CMTime, może to być ważne. Czy chcesz na przykład minuty: sekundy: klatki?)

W zależności od tego, jak chcesz prezentować CMTimewartości w swoim interfejsie użytkownika, może być lepiej wyodrębnić wartość sekund i przekazać ją NSDateFormatterlub NSDateComponentsFormattertak, aby uzyskać odpowiednią obsługę ustawień regionalnych.

rickster
źródło
10

Przywróć prostą składnię modulo w Swift 3:

Ta składnia została faktycznie zasugerowana na oficjalnej, szybkiej liście mailingowej Apples , ale z jakiegoś powodu zdecydowali się na mniej elegancką składnię.

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

Ta prosta wskazówka dotycząca migracji Swift 3 jest częścią bardziej kompleksowego przewodnika migracji Swift 3 z wieloma spostrzeżeniami (35 tys. Loc / 8 dni migracji) http://eon.codes/blog/2017/01/12/swift-3-migration /

eonista
źródło
1
To A jest w porządku, dostarcza ciekawych informacji, a także próbuje odpowiedzieć na pytanie.
Jakub Truhlář
3
@Jakub Truhlář ... Man, Thx. IMO To była moja najlepsza szybka poprawka migracji 3. Nie mogę uwierzyć, że ludzie go odrzucili. Modulo jest tak ważną koncepcją i jest rozważane w każdej książce kodowej, która ma arytmetykę. Uczynienie go rozwlekłym nie ma sensu, ponieważ arytmetyka w kodzie powinna być zapisana tak zwięźle, jak to możliwe. Ponieważ nasza zdolność poznawcza do rozumienia arytmetyki wzrasta, gdy widzisz pełny obraz, w przeciwieństwie do zrozumienia, co oznaczają poszczególne zmienne. IMO Kompleksowe nazewnictwo zmiennych jest ważne w logice biznesowej, ale nie w arytmetyce, wręcz przeciwnie.
eonista
2
@GitSync Modulo to ważna koncepcja, ale istnieje tylko dla liczb całkowitych. Powinieneś zrozumieć różnicę.
Sulthan
5
@GitSync Operacja modulo istnieje tylko dla liczb całkowitych. Mówisz o pozostałej części. Wartości dziesiętne mają dwa typy reszt. Dlatego Swift zdecydował się wyjaśnić tę operację. Niezbyt często oblicza się resztę całkowitą ( resztę obciętą ) na podwójnych wartościach.
Sulthan
1
@GitSync Jest remainder(dividingBy:)i .Możesz truncatingRemainder(dividingBy:)chcieć przeczytać dokumenty dla obu. Odnieś się również do tego samego pytania w C ++ stackoverflow.com/questions/6102948/ ...
Sulthan
2

Odkryłem, że w Swift 3 działa:

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

gdzie totalSecondsjest TimeInterval( Double).

benwiggy
źródło
3
Mieszanie wartości floor i round nie jest dobrym pomysłem, np. totalSeconds = 59.8Twój kod oblicza 0 minut i 0 sekund.
Martin R
Tak, masz rację. W rzeczywistości wcale roundnie jest potrzebne.
benwiggy
2

Nie ma potrzeby tworzenia oddzielnego operatora modulo dla liczb zmiennoprzecinkowych, chyba że uważasz, że czyni to kod bezpieczniejszym. Możesz przeciążać %operatora, aby akceptował liczby zmiennoprzecinkowe, na przykład:

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

Stosowanie

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

Możesz teraz używać %dowolnych dwóch liczb zmiennoprzecinkowych tego samego typu.

Peter Schorn
źródło