Nicola Prada - 1 year ago 76

Swift Question

I need to round stocks, indices and futures prices to the nearest tick. The first step is to look if the price is a multiple of the tick. Apple docs says "*Unlike the remainder operator in C and Objective-C, Swiftâ€™s remainder operator can also operate on floating-point numbers*".

If I write the following code in a playground or in a console app and I run it, I expect **0** as result but I get a remainder value equals to **0.00999999999999775**:

`var stringPrice = "17.66"`

var price = Double(stringPrice)

var tickSize: Double = 0.01

let remainder = price! % ticksize

This problem breaks my rounding function when using values such 17.66 as aPrice and 0.01 as 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

}

What is the best way to fix this?

Answer Source

Following the comments about the broken floating point math and the need to avoid floats and doubles for all the operations concerning money I changed my code to perform the remainder operation using NSDecimalNumbers. This seems to solve the precision problem.

```
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)")
```