"% İs unavailable: Bunun yerine truncatingRemainder kullan" ne anlama geliyor?


105

Bir uzantı için kod kullanırken aşağıdaki hatayı alıyorum, sadece farklı bir operatör mü kullanmak istiyorlar yoksa ifadedeki değerleri internet aramasına göre mi değiştiriyorlar emin değilim.

Hata:% kullanılamıyor: Bunun yerine truncatingRemainder kullanın

Uzantı kodu:

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

Dakika ve saniye değişkenlerini ayarlarken hata (lar) oluşur.


1
CMTimeGetSeconds'ın float döndürdüğünü düşünüyorum
zombi

3
Bu, %operatörün kullanılamadığı ve truncatingRemainderbunun yerine yöntem gibi bir şey kullanmayı düşünmeniz gerektiği anlamına gelir .
matt

1
Üzerinde modülo kullanamazsınız Float64ancak üzerinde Intsadece; bu nedenle: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60doğru yoldur.
holex

@holex. Hatalısınız. Modulo operatörünü yalnızca uygun tiplere sahip işlenenler üzerinde kullanabilirsiniz, yalnızca BinaryIntegerdeğil Int.
Peter Schorn

@PeterSchorn, 3 yıllık bir yorumu düzelttiğiniz için teşekkürler - bu protokol o sırada mevcut değildi.
holex

Yanıtlar:


174

CMTimeGetSeconds()bir kayan nokta sayısı ( Float64aka Double) döndürür . Swift 2'de bir kayan nokta bölümünün kalanını şu şekilde hesaplayabilirsiniz:

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

Swift 3'te bu,

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

Kodunuza uygulandı:

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

Bununla birlikte, bu özel durumda, ilk etapta süreyi bir tam sayıya dönüştürmek daha kolaydır:

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

Sonra sonraki satırlar basitleştiriyor

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

24

%Modülü operatörü sadece tamsayı türleri için tanımlanmıştır. Kayan nokta türleri için, istediğiniz IEEE 754 bölme / kalan davranış türü hakkında daha spesifik olmanız gerekir, bu nedenle bir yöntemi çağırmanız gerekir: ya remainderda truncatingRemainder. (Eğer kayan nokta matematiği yapıyorsanız, aslında bunu ve diğer pek çok şeyi önemsemeniz gerekir. , aksi takdirde beklenmedik / kötü sonuçlar alabilirsiniz.)

Gerçekten tamsayı modülü yapmayı düşünüyorsanız, CMTimeGetSecondskullanmadan önce dönüş değerini tam sayıya dönüştürmeniz gerekir %. (Bunu yaparsanız, nerede kullandığınıza bağlı olarak kesirli saniyeleri kısaltacağınızı unutmayın.CMTime bu önemli olabilir. Dakika mı istiyorsun: saniye: kare mi?)

CMTimeKullanıcı arabiriminizde değerleri nasıl sunmak istediğinize bağlı olarak , saniye değerini çıkarmanız ve bunu iletmeniz daha iyi olabilir NSDateFormatterveya NSDateComponentsFormatterböylece uygun yerel ayar desteği alırsınız.


10

Swift 3'te basit modulo sözdizimini geri getirin:

Bu sözdizimi aslında burada Apple'ın resmi hızlı posta listesinde önerildi, ancak nedense daha az zarif bir sözdizimi seçtiler.

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

Bu basit hızlı 3 geçiş ipucu, birçok bilgi içeren (35 bin yerel / 8 günlük geçiş) daha kapsamlı bir hızlı 3 geçiş kılavuzunun bir parçasıdır http://eon.codes/blog/2017/01/12/swift-3-migration /


1
Bu A iyi, ilginç bilgiler sağlıyor ve ayrıca Q'ya cevap vermeye çalışıyor.
Jakub Truhlář

3
@Jakub Truhlář ... Adam, Thx. IMO Bu, en hızlı 3 geçiş düzeltmemdi. İnsanların olumsuz oy verdiğine inanamıyorum. Modulo çok önemli bir kavramdır ve aritmetiği olan her kod kitabında düşünülür. Kodda aritmetik mümkün olduğunca kısaltılmış olarak yazılması gerektiğinden, onu ayrıntılı yapmak bir anlam ifade etmiyor. Aritmetiği anlama konusundaki bilişsel becerimiz, tek tek değişkenlerin ne anlama geldiğini anlamanın aksine, resmin tamamını gördüğünüzde artar. IMO Kapsamlı değişken adlandırma iş mantığında önemlidir, ancak aritmetik için değil, tam tersi.
eonist

2
@GitSync Modulo önemli bir kavramdır ancak yalnızca tamsayılar için mevcuttur. Farkı anlamalısın.
Sulthan

5
@GitSync Modulo işlemi sadece tamsayılar için mevcuttur. Kalan hakkında konuşuyorsunuz. Ondalık değerlerin iki tür kalanı vardır. Swift bu yüzden operasyonu netleştirmeye karar verdi. Çift değerlerde kalan tamsayı hesaplamak ( kalanı keserek ) çok yaygın değildir .
Sulthan

1
@GitSync var remainder(dividingBy:)ve truncatingRemainder(dividingBy:). Her ikisi için de dokümanları okumak isteyebilirsiniz. Ayrıca, C ++ stackoverflow.com/questions/6102948/…
Sulthan

2

Swift 3'te aşağıdakilerin çalıştığını buldum:

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

totalSecondsa TimeInterval( Double) nerede .


3
Zemin ve turu karıştırmak iyi bir fikir değildir, örneğin totalSeconds = 59.8kodunuz için 0 dakika ve 0 saniye hesaplar.
Martin R

Evet haklısın. Aslında roundbuna hiç gerek yok.
benwiggy

2

Kodu daha güvenli hale getirdiğini düşünmediğiniz sürece, kayan nokta numaraları için ayrı bir modulo operatörü oluşturmanıza gerek yoktur. %Operatörü kayan nokta sayılarını şu şekilde kabul etmesi için aşırı yükleyebilirsiniz :

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

Kullanım

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

Artık %aynı bağın herhangi iki kayan nokta numarasıyla kullanabilirsiniz .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.