你好我做了一个" Clicker"作为第一个项目,学习swift我有一个自动计时器,应该从其他数字中删除一些数字,但有时我得到像0.600000000000001的值,我不知道为什么。
这是我的#34;攻击"从僵尸的生命值中移除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
}
...
}
这是我的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
}
}
最后这里是变量值:
var zHealth = 10.0
var zSize = 10.0
var pAttack = 1
var pPengar = 0
var pPengarut = 1
当计时器打开且功能正在运行时我单击按钮我有时会得到奇怪的值,如0.600000000000001,如果我将函数中的0.2设置为0.25,有时会得到0.0999999999999996。我想知道为什么会发生这种情况以及如何处理它。
答案 0 :(得分:3)
这是因为浮点舍入错误。
如需进一步阅读,请参阅What Every Computer Scientist Should Know About Floating-Point Arithmetic。
将无数多个实数压缩成有限数量的位 需要近似的表示。虽然有无限的 许多整数,在大多数程序中,整数计算的结果都可以 以32位存储。相反,给定任意数量的位, 大多数带有实数的计算都会产生数量 不能使用那么多位来精确表示。因此 浮点计算的结果通常必须按顺序舍入 以适应其有限的表示。这个舍入误差是 浮点计算的特征。
答案 1 :(得分:2)
在trojanfoe's answer中,他shares a link描述了有关浮点数舍入问题的来源。
就做什么而言,有很多方法:
您可以切换到整数类型。例如,如果您的现有值都可以用最多两个小数位表示,那么将它们乘以100,然后在任何地方使用Int
类型,从您的Double
和Float
表示中删除代码
您可以简单地处理Double
类型引入的非常小的变体。例如:
如果在用户界面中显示结果,请使用NumberFormatter
使用指定的小数位数将Double
值转换为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)!)
顺便说一句,NSNumberFormatter
也享有另一个好处,即它尊重用户的本地化设置。例如,如果用户位于德国,其中小数位用,
而不是.
表示,则NSNumberFormatter
将使用用户的原生数字格式。
在测试数字是否等于某个值时,而不是仅仅使用==
运算符,请查看两个值之间的差异,看看它们是否在某个允许的舍入阈值内。< / p>
您可以使用Decimal
/ NSDecimalNumber
,在处理小数时不会出现舍入问题:
var value = Decimal(string: "1.0")!
value -= Decimal(string: "0.9")!
value -= Decimal(string: "0.1")!
或者:
var value = Decimal(1)
value -= Decimal(sign: .plus, exponent: -1, significand: 9)
value -= Decimal(sign: .plus, exponent: -1, significand: 1)
或者:
var value = Decimal(1)
value -= Decimal(9) / Decimal(10)
value -= Decimal(1) / Decimal(10)
注意,我明确避免使用任何Double
值,例如Decimal(0.1)
,因为从小数Decimal
创建Double
只捕获任何不精确Double
所需的值,以上三个例子完全避免了这一点。