1

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?

nico9T
  • 2,496
  • 2
  • 26
  • 44
  • 3
    This is the same issue as in [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – `17.66` cannot be represented exactly as a binary floating point number. – Martin R Jul 08 '16 at 08:41
  • 3
    Use `NSDecimalNumber` for any operations concerning money, avoid floats and doubles. Use `NSNumberFormatter` to present decimal values to the user. Don't round values by yourself, it's a much more complicated problem than you might think. – Sulthan Jul 08 '16 at 08:45

1 Answers1

1

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)")
nico9T
  • 2,496
  • 2
  • 26
  • 44