Это происходит потому, что лежащие в основе вычисления являются двоичными, а не десятичными.
Мы можем использовать Десятичное число (начиная с Swift 3), чтобы добиться большего успеха.Вот быстрая и грязная мод-функция для иллюстрации:
// 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
Так что все же следует сделать осторожное округление и / или учесть небольшую погрешность "эпсилон".(Сравнение с точными числами, такими как 0, вероятно, не очень хорошая идея).