Как обычно (см. 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
! И, конечно, последнее утверждение ложно.
Вам просто придется компенсировать это в своем коде. Очевидно, что остаток не может быть делителем, поэтому, если остаток исчезающе близко к делителю (в пределах некоторого эпсилона), вам просто нужно игнорировать его и назвать его нулем.
Вы можете отправить сообщение об ошибке, но я сомневаюсь, что с этим можно многое сделать.
Хорошо, я поставил запрос об этом и получил обратно, что он ведет себя как задумано,как я и подозревал. Ответ (от Стивен Кэнон ) был:
Это правильное поведение. 0,05 является двойным со значением 0,05000000000000000277555756156289135105907917022705078125. Деление 0.5 на это значение в точной арифметике дает 9 с остатком 0.04999999999999997501998194593397784046828746795654296875, который является именно тем результатом, который вы видите.
Единственная ошибка округления, которая возникает в вашем примере, заключается в цене / тике деления, котораяокругляется до 10 до того, как ваш .rounded(.towardZero)
получит шанс вступить в силу. Мы добавим API, чтобы в какой-то момент вы могли сделать что-то вроде price.divided(by: tick, rounding: .towardZero)
, что исключит это округление, но поведение truncatingRemainder
точно соответствует назначению.
Вы действительно хотите иметь либодесятичный тип (также в списке дел) или масштабирование задачи до десяти, чтобы ваш делитель стал точным:
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