【问题标题】:Swift Remainder function and RoundingSwift 余数函数和舍入
【发布时间】:2019-05-14 16:01:49
【问题描述】:

我正在制作自己的特定图形框架,但在处理一些简单的余数 if 语句时遇到了麻烦。

我希望 X 轴是对数的。每次用户平移图表时,它都会通过 translation.x 量更改 X-Min 和 X-Max 值。

我有以下代码

for i in Int(xMin)...Int(xMax) {
    let logValue = Double(log10(Float(i)))
    let rounded = logValue.rounded(toPlaces:3)

    if rounded.truncatingRemainder(dividingBy: 0.1) == 0 {
        //Draw division line
        print("Got Here")
    }
}

extension Double {
    /// Rounds the double to decimal places value
    func rounded(toPlaces places:Int) -> Double {
        let divisor = pow(10.0, Double(places))
        return (self * divisor).rounded() / divisor
    }
}

如果我在代码中设置断点,我可以看到如果舍入的值为 1.3,则代码永远不会到达打印行。如果我手动输入四舍五入的值是 100,那么打印线永远不会到达。

到目前为止,我对整数使用 % 余数运算符没有任何问题,但是无法弄清楚我哪里出错了。

【问题讨论】:

    标签: swift double rounding division


    【解决方案1】:

    这是因为底层计算是二进制而不是十进制。

    我们可以使用Decimal(从 Swift 3 开始)做得更好。这是一个快速而肮脏的 mod 函数用于说明:

    // a mod b = a - b*floor(a/b)
    func mod(_ a: Decimal,_ b: Decimal) -> Decimal {
        var d = a/(b)
        var f : Decimal = 0
        NSDecimalRound(&f, &d, 0, .down)
        return a-(b*(f))
    }
    

    普通浮点计算:

    var i = 1.3
    var j = 0.1
    i.truncatingRemainder(dividingBy: j) // 0.09999999999999998
    
    i = 1.3
    j = 0.15
    i.truncatingRemainder(dividingBy: j) // 0.1000000000000001
    

    带十进制:

    var a : Decimal = 1.3
    var b : Decimal = 0.1
    mod(a, b) // 0
    
    a = 1.3
    b = 0.15
    mod(a, b) // 0.1
    

    我们仍然需要小心:

    var a : Decimal = 0.906 // 0.9060000000000002048 conversion to decimal issue
    
    // Specify the significant digits directly
    a = Decimal(sign: FloatingPointSign.plus, exponent: -3, significand: Decimal(906)) // 0.906
    mod(a, Decimal(0.1)) // 0.006
    

    因此,仍应小心舍入和/或允许一些小的“epsilon”误差范围。 (与 0 这样的精确数字进行比较可能不是一个好主意)。

    【讨论】:

      【解决方案2】:

      永远记住,计算机上的浮点数是乱七八糟的,因为途中会出现各种舍入错误。我认识的许多程序员,包括我自己,都倾向于认为除以 10(十进制)是一个干净的操作。问题只是像 fx 0.1 这样的常量转换为二进制浮点数的舍入数。有关更多技术说明,请参阅https://en.wikipedia.org/wiki/Round-off_error#Real_world_example:_Patriot_Missile_Failure_due_to_magnification_of_roundoff_error

      一般来说,避免对浮点数进行相等比较,因为舍入错误总是会妨碍您。

      如果您像这样打印出 truncatingRemainder 调用的结果:

      let rounded = logValue.rounded(toPlaces:3)
      
      let remain = rounded.truncatingRemainder(dividingBy: 0.1)
      
      print("rounded \(rounded)   remain \(remain)")
      

      你会得到效果

      rounded 0.845   remain 0.04499999999999993
      rounded 0.903   remain 0.002999999999999975
      rounded 0.954   remain 0.05399999999999991
      rounded 1.0   remain 0.09999999999999995
      rounded 1.041   remain 0.04099999999999987
      rounded 1.079   remain 0.0789999999999999
      rounded 1.114   remain 0.01400000000000004
      

      请注意,即使是干净的 1.0 常量也会导致 truncatingRemainder(dividingBy: 0.1) 的结果混乱。

      【讨论】:

      • 谢谢你,我已经设法通过使用“小于”标准使其工作,但这似乎仍然是一种解决方法,并邀请其他“不需要的”值通过.
      猜你喜欢
      • 2020-11-09
      • 1970-01-01
      • 1970-01-01
      • 2011-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-19
      相关资源
      最近更新 更多