Swift 余数运算符精度

Swift Remainder operator precision

我需要将股票、指数和期货价格四舍五入到最接近的报价点。第一步是查看价格是否是价格变动的倍数。 Apple 文档说“与 C 和 Objective-C 中的余数运算符不同,Swift 的余数运算符也可以对浮点数进行运算”。

如果我在 playground 或控制台应用程序中编写以下代码并且我 运行 它,我希望结果为 0 但我得到的余数等于到 0.00999999999999775:

var stringPrice = "17.66"
var price = Double(stringPrice)
var tickSize: Double = 0.01

let remainder = price! % ticksize

当使用 17.66 作为 aPrice 和 0.01 作为 aTickSize 时,这个问题破坏了我的舍入函数:

func roundPriceToNearestTick(Price aPrice: Double, TickSize a TickSize: Double)-> Double{
    let remainder = aPrice % aTickSize
    let shouldRoundUp = remainder >= aTickSize/2 ? true : false
    let multiple = floor(aPrice/aTickSize)
    let returnPrice = !shouldRoundUp ? aTickSize*multiple : aTickSize*multiple + aTickSize
    return returnPrice
}

解决此问题的最佳方法是什么?

根据有关损坏的浮点数学的评论以及需要避免所有与金钱有关的操作的浮点数和双精度数的评论,我更改了我的代码以使用 NSDecimalNumbers 执行余数操作。这似乎解决了精度问题。

var stringPrice = "17.66"
var tickSizeDouble : Double = 0.01
var tickSizeDecimalNumber: NSDecimalNumber = 0.01

func decimalNumberRemainder(Dividend aDividend: NSDecimalNumber, Divisor aDivisor: NSDecimalNumber)->NSDecimalNumber{

    let behaviour = NSDecimalNumberHandler(roundingMode: NSRoundingMode.RoundDown,
                                                  scale: 0,
                                       raiseOnExactness: false ,
                                        raiseOnOverflow: false,
                                       raiseOnUnderflow: false,
                                    raiseOnDivideByZero: false )

    let quotient = aDividend.decimalNumberByDividingBy(aDivisor, withBehavior: behaviour)
    let subtractAmount = quotient.decimalNumberByMultiplyingBy(aDivisor)
    let remainder = aDividend.decimalNumberBySubtracting(subtractAmount)
    return remainder
}


let doubleRemainder = Double(stringPrice)! % tickSizeDouble
let decimalRemainder = decimalNumberRemainder(Dividend: NSDecimalNumber(string: stringPrice), Divisor:tickSizeDecimalNumber)


print("Using Double: \(doubleRemainder)")
print("Using NSDecimalNumber: \(decimalRemainder)")