2016-07-08 2 views
0

Мне нужно округлить цены акций, индексы и фьючерсы до ближайшего тика. Первый шаг - посмотреть, является ли цена кратной тика. Apple docs говорит: «В отличие от оператора остатка в C и Objective-C оператор остатка Swift также может работать с номерами с плавающей запятой».Свифт Оператор оператора

Если я пишу следующий код в детской площадке или в консольном приложении, и я запустить его, я ожидаю как результат, но я получаю значение остатка равно +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 
} 

Что такое лучший способ это исправить?

+3

Это та же проблема, что и в (http://stackoverflow.com/questions/588004/is-floating-point-math-broken) [Является плавающей точкой математике сломана?] - '17.66' не может быть представленный точно как двоичное число с плавающей запятой. –

+3

Используйте 'NSDecimalNumber' для любых операций, связанных с деньгами, во избежание плавания и удвоения. Используйте 'NSNumberFormatter' для представления десятичных значений пользователю. Не округлите значения самостоятельно, это гораздо более сложная проблема, чем вы могли бы подумать. – Sulthan

ответ

0

Следуя комментариям о сломанной математике с плавающей запятой и необходимости избегать поплавков и парных чисел для всех операций, связанных с деньгами, я изменил свой код, чтобы выполнить операцию остатка, используя 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)")