在 Double 中获得奇怪的值

Lil*_*kaN 3 floating-point ios swift

你好,我在学习 swift 时将“Clicker”作为第一个项目我有一个自动计时器,它应该从其他数字中删除一些数字,但有时我会得到像 0.600000000000001 这样的值,我不知道为什么。

这是我的“攻击”函数,它从僵尸的生命值中移除 0.2。

let fGruppenAttackTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("fGruppenAttackTime"), userInfo: nil, repeats: true)

func fGruppenAttackTime() {
    zHealth -= 0.2 
    if zHealth <= 0 {
        zHealth = zSize
        pPengar += pPengarut
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

这是我的attackZ按钮,应该从僵尸的健康中删除 1

@IBAction func attackZ(sender: UIButton) {
    zHealth -= Double(pAttack)
    fHunger -= 0.05
    fGruppenHunger.progress = Float(fHunger / 100)
    Actionlbl.text = ""
    if zHealth <= 0 {
        zHealth = zSize
        pPengar += pPengarut
    }
}
Run Code Online (Sandbox Code Playgroud)

最后这里是变量值:

var zHealth = 10.0
var zSize = 10.0
var pAttack = 1
var pPengar = 0
var pPengarut = 1
Run Code Online (Sandbox Code Playgroud)

当计时器打开且函数正在运行时,我单击按钮有时会得到像 0.600000000000001 这样的奇怪值,如果我将函数中的 0.2 设置为 0.25,有时我会得到 0.0999999999999996。我想知道为什么会发生这种情况以及如何处理它。

Rob*_*Rob 6

trojanfoe's answer 中,他分享了一个链接该链接描述了有关浮点数舍入问题的根源。

至于要做什么,有多种方法:

  1. 您可以转换为整数类型。例如,如果您现有的值都可以用最多两位小数表示,请将这些值乘以 100,然后Int在任何地方使用类型,从代码中删除DoubleFloat表示。

  2. 您可以简单地处理Double类型引入的非常小的变化。例如:

    • 如果在 UI 中显示结果,请使用指定的小数位数NumberFormatterDouble值转换为 a String

      let formatter = NumberFormatter()
      formatter.maximumFractionDigits = 2
      formatter.minimumFractionDigits = 0  // or you might use `2` here, too
      formatter.numberStyle = .decimal
      
      print(formatter.string(for: value)!)
      
      Run Code Online (Sandbox Code Playgroud)

      顺便说一句,它也NSNumberFormatter享有另一个好处,即它尊重用户的本地化设置。例如,如果用户居住在德国,小数位用 a,而不是 a 表示.NSNumberFormatter则将使用用户的本地数字格式。

    • 在测试一个数字是否等于某个值时,而不是仅使用==运算符,请查看两个值之间的差异并查看它们是否在某个允许的舍入阈值内。

  3. 您可以使用Decimal/ NSDecimalNumber,它在处理小数时不会遇到舍入问题:

    var value = Decimal(string: "1.0")!
    value -= Decimal(string: "0.9")!
    value -= Decimal(string: "0.1")!
    
    Run Code Online (Sandbox Code Playgroud)

    或者:

    var value = Decimal(1)
    value -= Decimal(sign: .plus, exponent: -1, significand: 9)
    value -= Decimal(sign: .plus, exponent: -1, significand: 1)
    
    Run Code Online (Sandbox Code Playgroud)

    或者:

    var value = Decimal(1)
    value -= Decimal(9) / Decimal(10)
    value -= Decimal(1) / Decimal(10)
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,我明确避免使用任何Double值,例如Decimal(0.1)因为Decimal从小数创建 aDouble只会捕获任何不精确性Double,而上面的三个示例完全避免了这种情况。


tro*_*foe 5

这是因为浮点舍入错误。

如需进一步阅读,请参阅每个计算机科学家应该了解的浮点运算知识

将无限多个实数压缩为有限数量的位数需要近似表示。虽然整数有无限多个,但在大多数程序中,整数计算的结果可以存储在 32 位中。相反,给定任何固定的位数,大多数实数计算将产生无法使用这么多位数精确表示的数量。因此,浮点计算的结果通常必须进行舍入,以适应其有限表示。这种舍入误差是浮点计算的特征。