【问题标题】:What does "% is unavailable: Use truncatingRemainder instead" mean?“% 不可用:改用 truncatingRemainder”是什么意思?
【发布时间】:2017-03-22 13:47:23
【问题描述】:

我在为扩展程序使用代码时收到以下错误,我不确定他们是否要求仅使用不同的运算符或根据互联网搜索修改表达式中的值。

错误:% 不可用:改用 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)
        }
    }
}

设置分钟和秒变量时发生错误。

【问题讨论】:

  • 我认为 CMTimeGetSeconds 返回浮点数
  • 这意味着% 运算符不可用,您应该考虑改用truncatingRemainder 之类的方法。
  • 你不能在 Float64 上使用模数,而只能在 Int 上使用;因此:let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60 是正确的方法。
  • @holex。你错了。您只能在类型符合 BinaryInteger 的操作数上使用模运算符,而不仅仅是 Int
  • @PeterSchorn,感谢您更正了 3 年前的评论 - 该协议当时根本不可用。

标签: ios swift swift3 modulus


【解决方案1】:

没有必要为浮点数创建单独的模运算符,除非您认为它可以使代码更安全。您可以重载 % 运算符以接受浮点数,如下所示:

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

用法

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

您现在可以将% 与任意两个相同类型的浮点数一起使用。

【讨论】:

    【解决方案2】:

    在 swift 3 中带回简单的模数语法:

    这种语法实际上是在 Apple 的官方 swift 邮件列表 here 上提出的,但出于某种原因,他们选择了一种不太优雅的语法。

    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 迁移指南的一部分,其中包含许多见解(35k loc / 8 天迁移)http://eon.codes/blog/2017/01/12/swift-3-migration/

    【讨论】:

    • 这个 A 很好,提供了有趣的信息并尝试回答 Q。
    • @Jakub Truhlář ...伙计,谢谢。 IMO 这是我最好的 swift 3 迁移修复。无法相信人们对它投了反对票。模数是一个如此重要的概念,并且在每本具有算术的代码书中都被考虑到。让它冗长没有意义,因为代码中的算术应该尽可能紧凑地编写。随着我们理解算术的认知能力的提高,当您可以看到完整的图片与理解单个变量的含义相反时。 IMO 综合变量命名在业务逻辑中很重要,但在算术中并不重要,恰恰相反。
    • @GitSync 模是一个重要的概念,但它只存在于整数。您应该了解其中的区别。
    • @GitSync 模运算仅对整数存在。你说的是余数。十进制值有两种余数。这就是为什么 Swift 决定明确操作。在 double 值上计算整数余数(截断余数)并不常见。
    • @GitSync 有remainder(dividingBy:)truncatingRemainder(dividingBy:)。您可能想阅读两者的文档。另外,请参阅 C++ stackoverflow.com/questions/6102948/… 的相同问题
    【解决方案3】:

    我发现以下在 Swift 3 中有效:

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

    其中totalSecondsTimeInterval (Double)。

    【讨论】:

    • 混合地板和圆形不是一个好主意,例如对于totalSeconds = 59.8,您的代码计算 0 分 0 秒。
    • 是的,你是对的。事实上,round 根本不需要。
    【解决方案4】:

    CMTimeGetSeconds() 返回一个浮点数 (Float64 aka 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
    

    【讨论】:

      【解决方案5】:

      % 模运算符仅针对整数类型定义。对于浮点类型,您需要更具体地了解您想要的 IEEE 754 除法/余数行为类型,因此您必须调用一个方法:remaindertruncatingRemainder。 (如果你在做浮点数学,你实际上需要关心这个,lots of other stuff,否则你会得到意想不到/糟糕的结果。)

      如果你真的打算做整数模,你需要在使用%之前将CMTimeGetSeconds的返回值转换为整数。 (请注意,如果你这样做,你会去掉小数秒......取决于你在哪里使用CMTime,这可能很重要。你想要分钟:秒:帧,例如?)

      根据您希望如何在 UI 中显示 CMTime 值,最好提取秒值并将其传递给 NSDateFormatterNSDateComponentsFormatter,以便获得适当的语言环境支持。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-25
        • 2013-07-24
        相关资源
        最近更新 更多