truncatingRemainder(dividingBy: ) 返回非零余数,即使数字是完全可整除的
truncatingRemainder(dividingBy: ) returning nonZero remainder even if number is completely divisible
我正在尝试使用 swift 的 truncatingRemainder(dividingBy:)
方法获取余数。
但是即使我使用的值可以被设计者完全整除,我得到的余数也不为零。我在这里尝试了很多可用的解决方案,但 none 有效。
P.S。我使用的值是 Double(也试过 Float)。
这是我的代码。
let price = 0.5
let tick = 0.05
let remainder = price.truncatingRemainder(dividingBy: tick)
if remainder != 0 {
return "Price should be in multiple of tick"
}
我得到 0.049999999999999975
作为余数,这显然不是预期的结果。
像往常一样(参见 https://floating-point-gui.de),这是由数字在计算机中的存储方式引起的。
根据文档,这正是我们所期望的
let price = //
let tick = //
let r = price.truncatingRemainder(dividingBy: tick)
let q = (price/tick).rounded(.towardZero)
tick*q+r == price // should be true
在你看来tick
平分price
的情况下,一切都取决于内部存储系统。例如,如果 price
是 0.4
并且 tick
是 0.04
,那么 r
会逐渐接近于零(如您所料)并且最后一个陈述为真.
但是当 price
是 0.5
并且 tick
是 0.05
时,由于数字的存储方式存在微小差异,我们最终得到这种奇怪的情况 r
不是消失在零附近,而是在 tick
附近消失!当然,最后一个陈述是错误的。
您只需在代码中进行补偿。显然余数不能是除数,所以如果余数 是 非常接近除数(在某个 epsilon 内),你只需要忽略它并 调用 为零。
您可以就此提交错误,但我怀疑是否可以对此做很多事情。
好的,我对此进行了查询,结果如我所料,它按预期运行。回复(来自 Stephen Canon)是:
That's the correct behavior. 0.05 is a Double with the value 0.05000000000000000277555756156289135105907917022705078125. Dividing 0.5 by that value in exact arithmetic gives 9 with a remainder of 0.04999999999999997501998194593397784046828746795654296875, which is exactly the result you're seeing.
The only rounding error that occurs in your example is in the division price/tick, which rounds up to 10 before your .rounded(.towardZero)
has a chance to take effect. We'll add an API to let you do something like price.divided(by: tick, rounding: .towardZero)
at some point, which will eliminate this rounding, but the behavior of truncatingRemainder
is precisely as intended.
You really want to have either a decimal type (also on the list of things to do) or to scale the problem by a power of ten so that your divisor become exact:
1> let price = 50.0
price: Double = 50
2> let tick = 5.0
tick: Double = 5
3> let r = price.truncatingRemainder(dividingBy: tick)
r: Double = 0
我正在尝试使用 swift 的 truncatingRemainder(dividingBy:)
方法获取余数。
但是即使我使用的值可以被设计者完全整除,我得到的余数也不为零。我在这里尝试了很多可用的解决方案,但 none 有效。
P.S。我使用的值是 Double(也试过 Float)。
这是我的代码。
let price = 0.5
let tick = 0.05
let remainder = price.truncatingRemainder(dividingBy: tick)
if remainder != 0 {
return "Price should be in multiple of tick"
}
我得到 0.049999999999999975
作为余数,这显然不是预期的结果。
像往常一样(参见 https://floating-point-gui.de),这是由数字在计算机中的存储方式引起的。
根据文档,这正是我们所期望的
let price = //
let tick = //
let r = price.truncatingRemainder(dividingBy: tick)
let q = (price/tick).rounded(.towardZero)
tick*q+r == price // should be true
在你看来tick
平分price
的情况下,一切都取决于内部存储系统。例如,如果 price
是 0.4
并且 tick
是 0.04
,那么 r
会逐渐接近于零(如您所料)并且最后一个陈述为真.
但是当 price
是 0.5
并且 tick
是 0.05
时,由于数字的存储方式存在微小差异,我们最终得到这种奇怪的情况 r
不是消失在零附近,而是在 tick
附近消失!当然,最后一个陈述是错误的。
您只需在代码中进行补偿。显然余数不能是除数,所以如果余数 是 非常接近除数(在某个 epsilon 内),你只需要忽略它并 调用 为零。
您可以就此提交错误,但我怀疑是否可以对此做很多事情。
好的,我对此进行了查询,结果如我所料,它按预期运行。回复(来自 Stephen Canon)是:
That's the correct behavior. 0.05 is a Double with the value 0.05000000000000000277555756156289135105907917022705078125. Dividing 0.5 by that value in exact arithmetic gives 9 with a remainder of 0.04999999999999997501998194593397784046828746795654296875, which is exactly the result you're seeing.
The only rounding error that occurs in your example is in the division price/tick, which rounds up to 10 before your
.rounded(.towardZero)
has a chance to take effect. We'll add an API to let you do something likeprice.divided(by: tick, rounding: .towardZero)
at some point, which will eliminate this rounding, but the behavior oftruncatingRemainder
is precisely as intended.You really want to have either a decimal type (also on the list of things to do) or to scale the problem by a power of ten so that your divisor become exact:
1> let price = 50.0
price: Double = 50
2> let tick = 5.0
tick: Double = 5
3> let r = price.truncatingRemainder(dividingBy: tick)
r: Double = 0