Що означає "% недоступний: замість цього використовувати truncatingRemainder"?


104

Я отримую таку помилку при використанні коду для розширення, я не впевнений, що вони просять просто використовувати інший оператор або змінити значення у виразі на основі пошуку в Інтернеті.

Помилка:% недоступно: замість цього використовуйте truncatingRemainder

Код розширення:

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

Помилка (и) виникають під час встановлення змінних хвилин і секунд.


1
Я думаю, що CMTimeGetSeconds повертається плаваючою
зомбі

3
Це означає, що %оператор недоступний, і вам слід truncatingRemainderзамість цього скористатися чимось на зразок методу.
мат

1
ви не можете використовувати модуль, Float64а Intлише він; отже: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60правильний шлях.
holex

Відповіді:


174

CMTimeGetSeconds()повертає номер з плаваючою комою ( Float64ака Double). У Swift 2 ви можете обчислити залишок поділу з плаваючою точкою як

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

У Swift 3 це робиться за допомогою

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

Застосовано до вашого коду:

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

Однак у цьому конкретному випадку в першу чергу простіше перетворити тривалість на ціле число:

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

Потім наступні рядки спростити до

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

24

Оператор %модуля визначається лише для цілих типів. Для типів з плаваючою комою ви повинні бути більш конкретними щодо типу IEEE 754 поведінки, що ви хочете, тому вам доведеться викликати метод: або remainderабо truncatingRemainder. (Якщо ви займаєтесь математикою з плаваючою комою, вам насправді потрібно подбати про це, і багато іншого , або ви можете отримати несподівані / погані результати.)

Якщо ви дійсно маєте намір робити цілий модуль, CMTimeGetSecondsперед використанням потрібно перетворити повернене значення на ціле число %. (Зверніть увагу, що якщо ви це зробите, ви вимкнете дробові секунди ... залежно від того, де ви використовуєте, CMTimeце може бути важливо. Чи хочете ви хвилини: секунди: кадри, наприклад?)

Залежно від того, як ви хочете представити CMTimeзначення у вашому інтерфейсі, може бути краще витягнути значення секунди та передати його NSDateFormatterабо NSDateComponentsFormatterтак, щоб ви отримали відповідну підтримку локалів.


10

Поверніть простий синтаксис модуля в швидкому 3:

Цей синтаксис був фактично запропонований в офіційному списку розсилки Apple, тут же вони чомусь вибрали менш елегантний синтаксис.

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

Цей простий підказник з швидкої міграції 3 є частиною всеосяжного швидкого міграційного посібника з багатьма уявленнями (35 к. Міс. / 8 днів міграції) http://eon.codes/blog/2017/01/12/swift-3-migration /


1
Це добре, надає цікаву інформацію, а також намагається відповісти на запитання
Jakub Truhlář

3
@Jakub Truhlář ... Людина, Thx. IMO Це було моїм найкращим швидким рішенням міграції 3. Не можу повірити, що люди це визнали голосом. Модуло - це така важлива концепція і продумується у кожній кодовій книзі, яка має арифметику. Зробити це багатослівним немає сенсу, оскільки Арифметика в коді повинна бути написана максимально компактно. Оскільки наша пізнавальна здатність розуміти арифметику зростає, коли ви можете бачити повну картину як протидію розумінню того, що означають окремі змінні. Комплексне іменування змінних IMO важливо в бізнес-логіці, але не в арифметиці, навпаки.
еоніст

2
@GitSync Modulo - важлива концепція, але вона існує лише для цілих чисел. Ви повинні зрозуміти різницю.
Султан

5
@GitSync Модульна операція існує лише для цілих чисел. Ви говорите про залишок. Десяткові значення мають два типи залишків. Тому Свіфт вирішив зробити операцію явною. Нераціонально обчислювати цілий залишок ( обрізання залишку ) на подвійні значення.
Султан

1
@GitSync Існує remainder(dividingBy:)і. truncatingRemainder(dividingBy:)Можливо, ви хочете прочитати документи для обох. Також зверніться до того ж питання для C ++ stackoverflow.com/questions/6102948/…
Sultahan

2

Я виявив, що в Swift 3 працюють такі:

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

де totalSecondsє TimeInterval( Double).


3
Змішати підлогу та круглу - це не дуже гарна ідея, наприклад, для totalSeconds = 59.8вашого коду обчислюється 0 хвилин та 0 секунд.
Martin R

Так, ви праві. Насправді це roundзовсім не потрібно.
benwiggy

1

Не потрібно створювати окремого оператора модуля для чисел з плаваючою комою, якщо ви не вважаєте, що це робить безпечнішим код. Ви можете перевантажити %оператора, щоб прийняти такі числа з плаваючою комою:

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

Використання

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

Тепер ви можете використовувати %будь-які два числа з плаваючою точкою одного і того ж відтінку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.