Что означает «% недоступен: используйте truncatingRemainder вместо»?

105

При использовании кода для расширения я получаю следующую ошибку: я не уверен, просят ли они просто использовать другой оператор или изменить значения в выражении на основе поиска в Интернете.

Ошибка:% недоступен: используйте 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 возвращает float
зомби
3
Это означает, что %оператор недоступен, и вам следует подумать об использовании чего-то вроде этого truncatingRemainderметода.
matt
1
нельзя использовать по модулю Float64только on Int; следовательно: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60это правильный путь.
holex
@holex. Вы неправы. Вы можете использовать оператор по модулю только для операндов, типы которых соответствуют BinaryInteger, а не только Int.
Питер Шорн
@PeterSchorn, спасибо за исправление комментария трехлетней давности - этот протокол в то время вообще не был доступен.
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

Верните простой синтаксис по модулю в Swift 3:

Этот синтаксис фактически предложил на яблоки официальный стремительной список рассылки здесь , но по какой - то причине они выбрали менее элегантного синтаксиса.

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

Этот простой совет по миграции на Swift 3 является частью более подробного руководства по миграции на Swift 3 со множеством идей (35 тыс. Локальных / 8 дней миграции) http://eon.codes/blog/2017/01/12/swift-3-migration /

эонист
источник
1
Этот А в порядке, предоставляет интересную информацию, а также пытается ответить на вопрос.
Якуб Трухларж
3
@ Якуб Трухларж ... Мужик, спасибо. IMO Это было мое лучшее исправление быстрой миграции 3. Не могу поверить, что люди проголосовали против. Модуло - это очень важное понятие, о котором думают в каждой кодовой книге, в которой есть арифметика. Делать его подробным не имеет смысла, поскольку арифметика в коде должна быть написана как можно компактнее. Поскольку наша когнитивная способность понимать арифметику возрастает, когда вы можете видеть полную картину, а не понимать, что означают отдельные переменные. IMO Комплексное именование переменных важно в бизнес-логике, но не в арифметике, как раз наоборот.
eonist
2
@GitSync Modulo - важная концепция, но она существует только для целых чисел. Вы должны понимать разницу.
Sulthan
5
@GitSync Операция по модулю существует только для целых чисел. Вы говорите об остатке. У десятичных значений есть два типа остатков. Вот почему Swift решил сделать операцию явной. Вычисление целочисленного остатка ( усечение остатка ) для значений типа double не очень распространено .
Sulthan
1
@GitSync Существует remainder(dividingBy:)и truncatingRemainder(dividingBy:). Возможно, вы захотите прочитать документацию для обоих. Также обратитесь к тому же вопросу для C ++ stackoverflow.com/questions/6102948/…
Sulthan
2

Я обнаружил, что в Swift 3 работает следующее:

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

где totalSecondsa TimeInterval( Double).

Бенвигги
источник
3
Смешивать пол и раунд - не лучшая идея, например, для totalSeconds = 59.8вашего кода вычисляется 0 минут и 0 секунд.
Martin R
Да, ты прав. Фактически, это roundвообще не нужно.
benwiggy
2

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

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

использование

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

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

Питер Шорн
источник