diff --git a/Foundation/NSDecimal.swift b/Foundation/NSDecimal.swift index c5a999f23f..c6e0961fc6 100644 --- a/Foundation/NSDecimal.swift +++ b/Foundation/NSDecimal.swift @@ -33,6 +33,9 @@ public struct Decimal { return UInt32((__lengthAndFlags & 0b0000_1111)) } set { + guard newValue <= maxMantissaLength else { + fatalError("Attempt to set a length greater than capacity \(newValue) > \(maxMantissaLength)") + } __lengthAndFlags = (__lengthAndFlags & 0b1111_0000) | UInt8(newValue & 0b0000_1111) @@ -88,9 +91,8 @@ public struct Decimal { public init(_exponent: Int32, _length: UInt32, _isNegative: UInt32, _isCompact: UInt32, _reserved: UInt32, _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)){ self._mantissa = _mantissa self.__exponent = Int8(truncatingBitPattern: _exponent) - self.__lengthAndFlags = 0 + self.__lengthAndFlags = UInt8(_length & 0b1111) self.__reserved = 0 - self._length = _length self._isNegative = _isNegative self._isCompact = _isCompact self._reserved = _reserved @@ -564,9 +566,7 @@ fileprivate func divideByShort(_ d: inout T, _ divisor:U d[i] = UInt16(accumulator / UInt32(divisor)) carry = accumulator % UInt32(divisor) } - while d._length != 0 && d[d._length - 1] == 0 { - d._length -= 1 - } + d.trimTrailingZeros() return (UInt16(carry),.noError) } @@ -674,7 +674,7 @@ fileprivate func fitMantissa(_ big: inout WideDecimal, _ exponent: inout Int32, var previousRemainder: Bool = false // Divide by 10 as much as possible - while big._length >= Decimal.maxSize { + while big._length > Decimal.maxSize + 1 { if remainder != 0 { previousRemainder = true } @@ -727,21 +727,19 @@ fileprivate func fitMantissa(_ big: inout WideDecimal, _ exponent: inout Int32, return .lossOfPrecision; } -fileprivate func integerMultiply(_ big: inout WideDecimal, - _ left: WideDecimal, - _ right: WideDecimal) -> NSDecimalNumber.CalculationError { +fileprivate func integerMultiply(_ big: inout T, + _ left: T, + _ right: Decimal) -> NSDecimalNumber.CalculationError { if left._length == 0 || right._length == 0 { big._length = 0 return .noError } - if big._length > left._length + right._length { - big._length = left._length + right._length + if big._length == 0 || big._length > left._length + right._length { + big._length = min(big.maxMantissaLength,left._length + right._length) } - for i in 0.. NSDecimalNumber.CalculationError { +fileprivate func integerDivide(_ r: inout T, + _ cu: T, + _ cv: Decimal) -> NSDecimalNumber.CalculationError { // Calculate result = a / b. // Result could NOT be a pointer to same space as a or b. // resultLen must be >= aLen - bLen. @@ -792,17 +788,19 @@ fileprivate func integerDivide(_ r: inout WideDecimal, var u = WideDecimal(true) var v = WideDecimal(true) // divisor - var v1:UInt16, v2:UInt16 - // Simple case if cv.isZero { return .divideByZero; } // If u < v, the result is approximately 0... - if cu < cv { - r._length = 0 - return .noError; + if cu._length < cv._length { + for i in 0.. 1 is probably useless, since optimizations // up there are taking over this case. I'll keep it, just in case. - v1 = v[v._length-1] - v2 = v._length > 1 ? v[v._length-2] : 0 + let v1:UInt16 = v[v._length-1] + let v2:UInt16 = v._length > 1 ? v[v._length-2] : 0 // D2: initialize j // On each pass, build a single value for the quotient. for j in 0..> 16 // multiplication carry acc = acc & 0xffff; - let uu = u // work around compiler bug - acc = 0xffff + UInt32(uu[ul - vl + i - UInt32(j) - UInt32(1)]) - acc + sk; // subtract + acc = 0xffff + UInt32(u[ul - vl + i - UInt32(j) - UInt32(1)]) - acc + sk; // subtract sk = acc >> 16; u[ul - vl + i - UInt32(j) - UInt32(1)] = UInt16(truncatingBitPattern:acc) } @@ -905,9 +906,7 @@ fileprivate func integerDivide(_ r: inout WideDecimal, for i in 0...v._length { let ul = u._length let vl = v._length - let vv = v // work around compiler bug - let uu = u // work around compiler bug - acc = UInt32(vv[i]) + UInt32(uu[UInt32(ul) - UInt32(vl) + UInt32(i) - UInt32(j) - UInt32(1)]) + k + acc = UInt32(v[i]) + UInt32(u[UInt32(ul) - UInt32(vl) + UInt32(i) - UInt32(j) - UInt32(1)]) + k k = acc >> 16; u[UInt32(ul) - UInt32(vl) + UInt32(i) - UInt32(j) - UInt32(1)] = UInt16(truncatingBitPattern:acc) } @@ -920,14 +919,12 @@ fileprivate func integerDivide(_ r: inout WideDecimal, r._length = UInt32(ql); - while r._length != 0 && r[r._length - 1] == 0 { - r._length -= 1 - } + r.trimTrailingZeros() return .noError; } -fileprivate func integerMultiplyByPowerOf10(_ result: inout WideDecimal, _ left: WideDecimal, _ p: Int) -> NSDecimalNumber.CalculationError { +fileprivate func integerMultiplyByPowerOf10(_ result: inout T, _ left: T, _ p: Int) -> NSDecimalNumber.CalculationError { var power = p if power == 0 { result = left @@ -943,10 +940,10 @@ fileprivate func integerMultiplyByPowerOf10(_ result: inout WideDecimal, _ left: var error:NSDecimalNumber.CalculationError = .noError while power > maxpow10 { - var big = WideDecimal() + var big = T() power -= maxpow10 - let p10 = WideDecimal(pow10[maxpow10]) + let p10 = pow10[maxpow10] if !isNegative { error = integerMultiply(&big,result,p10) @@ -965,9 +962,10 @@ fileprivate func integerMultiplyByPowerOf10(_ result: inout WideDecimal, _ left: result._length = big._length } - var big = WideDecimal() + var big = T() + // Handle the rest of the power (<= maxpow10) - let p10 = WideDecimal(pow10[Int(power)]) + let p10 = pow10[Int(power)] if !isNegative { error = integerMultiply(&big, result, p10) @@ -994,7 +992,7 @@ public func NSDecimalRound(_ result: UnsafeMutablePointer, _ number: Un public func NSDecimalNormalize(_ a: UnsafeMutablePointer, _ b: UnsafeMutablePointer, _ roundingMode: NSDecimalNumber.RoundingMode) -> NSDecimalNumber.CalculationError { var diffexp = a.pointee.__exponent - b.pointee.__exponent - var result = WideDecimal() + var result = Decimal() // // If the two numbers share the same exponents, @@ -1030,19 +1028,49 @@ public func NSDecimalNormalize(_ a: UnsafeMutablePointer, _ b: UnsafeMu // Try to multiply aa to reach the same exponent level than bb // - if integerMultiplyByPowerOf10(&result, WideDecimal(aa.pointee), Int(diffexp)) == .noError { + if integerMultiplyByPowerOf10(&result, aa.pointee, Int(diffexp)) == .noError { // Succeed. Adjust the length/exponent info // and return no errorNSDecimalNormalize - aa.pointee._length = result._length - for i in 0.. 0 multiply aa by the same value + // + if !bb.pointee.isZero { + _ = integerMultiplyByPowerOf10(&result, aa.pointee, Int(maxpow10)) + aa.pointee.copyMantissa(from: result) + aa.pointee._exponent -= Int32(maxpow10) + } else { + bb.pointee._exponent = aa.pointee._exponent; + } + + // + // the two exponents are now identical, but we've lost some digits in the operation. + // + return .lossOfPrecision; } public func NSDecimalAdd(_ result: UnsafeMutablePointer, _ leftOperand: UnsafePointer, _ rightOperand: UnsafePointer, _ roundingMode: NSDecimalNumber.RoundingMode) -> NSDecimalNumber.CalculationError { @@ -1233,9 +1261,7 @@ fileprivate func integerSubtract(_ result: inout Decimal, _ left: inout Decimal, } result._length = i; - while result._length != 0 && result[result._length - 1] == 0 { - result._length -= 1 - } + result.trimTrailingZeros() return .noError; } @@ -1265,7 +1291,7 @@ public func NSDecimalMultiply(_ result: UnsafeMutablePointer, _ leftOpe var big = WideDecimal() var calculationError:NSDecimalNumber.CalculationError = .noError - calculationError = integerMultiply(&big,WideDecimal(leftOperand.pointee),WideDecimal(rightOperand.pointee)) + calculationError = integerMultiply(&big,WideDecimal(leftOperand.pointee),rightOperand.pointee) result.pointee._isNegative = (leftOperand.pointee._isNegative + rightOperand.pointee._isNegative) % 2 @@ -1340,7 +1366,7 @@ public func NSDecimalDivide(_ result: UnsafeMutablePointer, _ leftOpera } _ = integerMultiplyByPowerOf10(&big, WideDecimal(a), 38) // Trust me, it's 38 ! - _ = integerDivide(&big, big, WideDecimal(b)) + _ = integerDivide(&big, big, b) _ = fitMantissa(&big, &exponent, .down) let length = min(big._length,Decimal.maxSize) @@ -1393,16 +1419,77 @@ public func NSDecimalString(_ dcm: UnsafePointer, _ locale: AnyObject?) fileprivate protocol VariableLengthNumber { var _length: UInt32 { get set } + init() subscript(index:UInt32) -> UInt16 { get set } var isZero:Bool { get } + mutating func copyMantissa(from other:T) + mutating func zeroMantissa() + mutating func trimTrailingZeros() + var maxMantissaLength: UInt32 { get } } extension Decimal: VariableLengthNumber { - + var maxMantissaLength:UInt32 { + return Decimal.maxSize + } + fileprivate mutating func zeroMantissa() { + for i in 0.. Decimal.maxSize { + _length = Decimal.maxSize + } + while _length != 0 && self[_length - 1] == 0 { + _length -= 1 + } + } + fileprivate mutating func copyMantissa(from other: T) { + if other._length > maxMantissaLength { + for i in maxMantissaLength..(from other: T) { + let length = other is Decimal ? min(other._length,Decimal.maxSize) : other._length + for i in 0.. \(maxMantissaLength)") } __length = UInt16(newValue) } } init(_ extraWide:Bool = false) { - __length = UInt16(Decimal.maxSize * 2) + __length = 0 _mantissa = (UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0),UInt16(0)) _extraWide = extraWide } @@ -1479,26 +1566,6 @@ fileprivate struct WideDecimal : VariableLengthNumber { } } } - public func compare(to rhs: WideDecimal) -> ComparisonResult { - if self._length > rhs._length { - return .orderedDescending - } - if self._length < rhs._length { - return .orderedAscending - } - for i in (0.. rhs[i] { - return .orderedDescending - } - if self[i] < rhs[i] { - return .orderedAscending - } - } - return .orderedSame - } - public static func <(lhs: WideDecimal, rhs: WideDecimal) -> Bool { - return lhs.compare(to:rhs) == .orderedAscending - } func toDecimal() -> Decimal { var result = Decimal() result._length = self._length @@ -1630,7 +1697,7 @@ extension Decimal { _exponent = newExponent; self.compact(); } - fileprivate func compare(to other:Decimal) -> ComparisonResult { + internal func compare(to other:Decimal) -> ComparisonResult { // NaN is a special case and is arbitrary ordered before everything else // Conceptually comparing with NaN is bogus anyway but raising or // always returning the same answer will confuse the sorting algorithms diff --git a/Foundation/NSDecimalNumber.swift b/Foundation/NSDecimalNumber.swift index 759dc286a3..a8b1db4ac7 100644 --- a/Foundation/NSDecimalNumber.swift +++ b/Foundation/NSDecimalNumber.swift @@ -75,12 +75,35 @@ public protocol NSDecimalNumberBehaviors { // Receiver can raise, return a new value, or return nil to ignore the exception. +fileprivate func handle(_ error: NSDecimalNumber.CalculationError, _ handler: NSDecimalNumberBehaviors) { + // handle the error condition, such as throwing an error for over/underflow +} /*************** NSDecimalNumber: the class ***********/ open class NSDecimalNumber : NSNumber { - - public convenience init(mantissa: UInt64, exponent: Int16, isNegative flag: Bool) { NSUnimplemented() } - public init(decimal dcm: Decimal) { NSUnimplemented() } + + fileprivate let decimal: Decimal + public convenience init(mantissa: UInt64, exponent: Int16, isNegative: Bool) { + var d = Decimal() + d._exponent = Int32(exponent) + d._isNegative = isNegative ? 1 : 0 + var man = mantissa + d._mantissa.0 = UInt16(man & 0xffff) + man >>= 4 + d._mantissa.1 = UInt16(man & 0xffff) + man >>= 4 + d._mantissa.2 = UInt16(man & 0xffff) + man >>= 4 + d._mantissa.3 = UInt16(man & 0xffff) + d._length = 4 + d.trimTrailingZeros() + // TODO more parts of the mantissa... + self.init(decimal: d) + } + public init(decimal dcm: Decimal) { + self.decimal = dcm + super.init() + } public convenience init(string numberValue: String?) { NSUnimplemented() } public convenience init(string numberValue: String?, locale: AnyObject?) { NSUnimplemented() } @@ -89,15 +112,19 @@ open class NSDecimalNumber : NSNumber { } public required convenience init(floatLiteral value: Double) { - NSUnimplemented() + self.init(decimal:Decimal(value)) } public required convenience init(booleanLiteral value: Bool) { - NSUnimplemented() + if value { + self.init(integerLiteral: 1) + } else { + self.init(integerLiteral: 0) + } } public required convenience init(integerLiteral value: Int) { - NSUnimplemented() + self.init(decimal:Decimal(value)) } public required convenience init(bytes buffer: UnsafeRawPointer, objCType type: UnsafePointer) { @@ -105,42 +132,120 @@ open class NSDecimalNumber : NSNumber { } open override func description(withLocale locale: Locale?) -> String { NSUnimplemented() } + + open class var zero: NSDecimalNumber { + return NSDecimalNumber(integerLiteral: 0) + } + open class var one: NSDecimalNumber { + return NSDecimalNumber(integerLiteral: 1) + } + open class var minimum: NSDecimalNumber { + return NSDecimalNumber(decimal:Decimal.leastFiniteMagnitude) + } + open class var maximum: NSDecimalNumber { + return NSDecimalNumber(decimal:Decimal.greatestFiniteMagnitude) + + } + open class var notANumber: NSDecimalNumber { + return NSDecimalNumber(decimal: Decimal.nan) + } - // TODO: "declarations from extensions cannot be overridden yet" - // Although it's not clear we actually need to redeclare this here when the extension adds it to the superclass of this class - // open var decimalValue: Decimal { NSUnimplemented() } - - open class var zero: NSDecimalNumber { NSUnimplemented() } - open class var one: NSDecimalNumber { NSUnimplemented() } - open class var minimum: NSDecimalNumber { NSUnimplemented() } - open class var maximum: NSDecimalNumber { NSUnimplemented() } - open class var notANumber: NSDecimalNumber { NSUnimplemented() } - - open func adding(_ decimalNumber: NSDecimalNumber) -> NSDecimalNumber { NSUnimplemented() } - open func adding(_ decimalNumber: NSDecimalNumber, withBehavior behavior: NSDecimalNumberBehaviors?) -> NSDecimalNumber { NSUnimplemented() } - - open func subtracting(_ decimalNumber: NSDecimalNumber) -> NSDecimalNumber { NSUnimplemented() } - open func subtracting(_ decimalNumber: NSDecimalNumber, withBehavior behavior: NSDecimalNumberBehaviors?) -> NSDecimalNumber { NSUnimplemented() } - - open func multiplying(by decimalNumber: NSDecimalNumber) -> NSDecimalNumber { NSUnimplemented() } - open func multiplying(by decimalNumber: NSDecimalNumber, withBehavior behavior: NSDecimalNumberBehaviors?) -> NSDecimalNumber { NSUnimplemented() } - - open func dividing(by decimalNumber: NSDecimalNumber) -> NSDecimalNumber { NSUnimplemented() } - open func dividing(by decimalNumber: NSDecimalNumber, withBehavior behavior: NSDecimalNumberBehaviors?) -> NSDecimalNumber { NSUnimplemented() } + open func adding(_ other: NSDecimalNumber) -> NSDecimalNumber { + return adding(other, withBehavior: nil) + } + open func adding(_ other: NSDecimalNumber, withBehavior b: NSDecimalNumberBehaviors?) -> NSDecimalNumber { + var result = Decimal() + var left = self.decimal + var right = other.decimal + let behavior = b ?? NSDecimalNumber.defaultBehavior + let roundingMode = behavior.roundingMode() + let error = NSDecimalAdd(&result, &left, &right, roundingMode) + handle(error,behavior) + return NSDecimalNumber(decimal: result) + } + + open func subtracting(_ other: NSDecimalNumber) -> NSDecimalNumber { + return subtracting(other, withBehavior: nil) + } + open func subtracting(_ other: NSDecimalNumber, withBehavior b: NSDecimalNumberBehaviors?) -> NSDecimalNumber { + var result = Decimal() + var left = self.decimal + var right = other.decimal + let behavior = b ?? NSDecimalNumber.defaultBehavior + let roundingMode = behavior.roundingMode() + let error = NSDecimalSubtract(&result, &left, &right, roundingMode) + handle(error,behavior) + return NSDecimalNumber(decimal: result) + } + open func multiplying(by other: NSDecimalNumber) -> NSDecimalNumber { + return multiplying(by: other, withBehavior: nil) + } + open func multiplying(by other: NSDecimalNumber, withBehavior b: NSDecimalNumberBehaviors?) -> NSDecimalNumber { + var result = Decimal() + var left = self.decimal + var right = other.decimal + let behavior = b ?? NSDecimalNumber.defaultBehavior + let roundingMode = behavior.roundingMode() + let error = NSDecimalMultiply(&result, &left, &right, roundingMode) + handle(error,behavior) + return NSDecimalNumber(decimal: result) + } - open func raising(toPower power: Int) -> NSDecimalNumber { NSUnimplemented() } - open func raising(toPower power: Int, withBehavior behavior: NSDecimalNumberBehaviors?) -> NSDecimalNumber { NSUnimplemented() } + open func dividing(by other: NSDecimalNumber) -> NSDecimalNumber { + return dividing(by: other, withBehavior: nil) + } + open func dividing(by other: NSDecimalNumber, withBehavior b: NSDecimalNumberBehaviors?) -> NSDecimalNumber { + var result = Decimal() + var left = self.decimal + var right = other.decimal + let behavior = b ?? NSDecimalNumber.defaultBehavior + let roundingMode = behavior.roundingMode() + let error = NSDecimalDivide(&result, &left, &right, roundingMode) + handle(error,behavior) + return NSDecimalNumber(decimal: result) + } - open func multiplying(byPowerOf10 power: Int16) -> NSDecimalNumber { NSUnimplemented() } - open func multiplying(byPowerOf10 power: Int16, withBehavior behavior: NSDecimalNumberBehaviors?) -> NSDecimalNumber { NSUnimplemented() } + open func raising(toPower power: Int) -> NSDecimalNumber { + return raising(toPower:power, withBehavior: nil) + } + open func raising(toPower power: Int, withBehavior b: NSDecimalNumberBehaviors?) -> NSDecimalNumber { + var result = Decimal() + var input = self.decimal + let behavior = b ?? NSDecimalNumber.defaultBehavior + let roundingMode = behavior.roundingMode() + let error = NSDecimalPower(&result, &input, power, roundingMode) + handle(error,behavior) + return NSDecimalNumber(decimal: result) + } + + open func multiplying(byPowerOf10 power: Int16) -> NSDecimalNumber { + return multiplying(byPowerOf10: power, withBehavior: nil) + } + open func multiplying(byPowerOf10 power: Int16, withBehavior b: NSDecimalNumberBehaviors?) -> NSDecimalNumber { + var result = Decimal() + var input = self.decimal + let behavior = b ?? NSDecimalNumber.defaultBehavior + let roundingMode = behavior.roundingMode() + let error = NSDecimalPower(&result, &input, Int(power), roundingMode) + handle(error,behavior) + return NSDecimalNumber(decimal: result) + } open func rounding(accordingToBehavior behavior: NSDecimalNumberBehaviors?) -> NSDecimalNumber { NSUnimplemented() } // Round to the scale of the behavior. - open override func compare(_ decimalNumber: NSNumber) -> ComparisonResult { NSUnimplemented() } // compare two NSDecimalNumbers - - open class var defaultBehavior: NSDecimalNumberBehaviors { NSUnimplemented() } + open override func compare(_ decimalNumber: NSNumber) -> ComparisonResult { + if let num = decimalNumber as? NSDecimalNumber { + return decimal.compare(to:num.decimal) + } else { + return decimal.compare(to:Decimal(decimalNumber.doubleValue)) + } + } + + open class var defaultBehavior: NSDecimalNumberBehaviors { + return NSDecimalNumberHandler.defaultBehavior + } // One behavior per thread - The default behavior is // rounding mode: NSRoundPlain // scale: No defined scale (full precision) @@ -150,7 +255,54 @@ open class NSDecimalNumber : NSNumber { open override var objCType: UnsafePointer { NSUnimplemented() } // return 'd' for double - open override var doubleValue: Double { NSUnimplemented() } + open override var int8Value: Int8 { + return Int8(decimal.doubleValue) + } + open override var uint8Value: UInt8 { + return UInt8(decimal.doubleValue) + } + open override var int16Value: Int16 { + return Int16(decimal.doubleValue) + } + open override var uint16Value: UInt16 { + return UInt16(decimal.doubleValue) + } + open override var int32Value: Int32 { + return Int32(decimal.doubleValue) + } + open override var uint32Value: UInt32 { + return UInt32(decimal.doubleValue) + } + open override var int64Value: Int64 { + return Int64(decimal.doubleValue) + } + open override var uint64Value: UInt64 { + return UInt64(decimal.doubleValue) + } + open override var floatValue: Float { + return Float(decimal.doubleValue) + } + open override var doubleValue: Double { + return decimal.doubleValue + } + open override var boolValue: Bool { + return !decimal.isZero + } + open override var intValue: Int { + return Int(decimal.doubleValue) + } + open override var uintValue: UInt { + return UInt(decimal.doubleValue) + } + + open override func isEqual(_ value: Any?) -> Bool { + if let number = value as? NSDecimalNumber { + return self.decimal == number.decimal + } else { + return false + } + } + } // return an approximate double value @@ -158,7 +310,26 @@ open class NSDecimalNumber : NSNumber { /*********** A class for defining common behaviors *******/ open class NSDecimalNumberHandler : NSObject, NSDecimalNumberBehaviors, NSCoding { - + + static let defaultBehavior = NSDecimalNumberHandler() + + let _roundingMode: NSDecimalNumber.RoundingMode + let _scale:Int16 + + let _raiseOnExactness: Bool + let _raiseOnOverflow: Bool + let _raiseOnUnderflow: Bool + let _raiseOnDivideByZero: Bool + + public override init() { + _roundingMode = .plain + _scale = Int16(NSDecimalNoScale) + + _raiseOnExactness = false + _raiseOnOverflow = true + _raiseOnUnderflow = true + _raiseOnDivideByZero = true + } public required init?(coder aDecoder: NSCoder) { NSUnimplemented() } @@ -167,24 +338,43 @@ open class NSDecimalNumberHandler : NSObject, NSDecimalNumberBehaviors, NSCoding NSUnimplemented() } - open class func `default`() -> NSDecimalNumberHandler { NSUnimplemented() } + open class func `default`() -> NSDecimalNumberHandler { + return defaultBehavior + } // rounding mode: NSRoundPlain // scale: No defined scale (full precision) // ignore exactnessException (return nil) // raise on overflow, underflow and divide by zero. - public init(roundingMode: NSDecimalNumber.RoundingMode, scale: Int16, raiseOnExactness exact: Bool, raiseOnOverflow overflow: Bool, raiseOnUnderflow underflow: Bool, raiseOnDivideByZero divideByZero: Bool) { NSUnimplemented() } + public init(roundingMode: NSDecimalNumber.RoundingMode, scale: Int16, raiseOnExactness exact: Bool, raiseOnOverflow overflow: Bool, raiseOnUnderflow underflow: Bool, raiseOnDivideByZero divideByZero: Bool) { + _roundingMode = roundingMode + _scale = scale + _raiseOnExactness = exact + _raiseOnOverflow = overflow + _raiseOnUnderflow = underflow + _raiseOnDivideByZero = divideByZero + } - open func roundingMode() -> NSDecimalNumber.RoundingMode { NSUnimplemented() } + open func roundingMode() -> NSDecimalNumber.RoundingMode { + return _roundingMode + } - open func scale() -> Int16 { NSUnimplemented() } - // The scale could return NO_SCALE for no defined scale. + // The scale could return NoScale for no defined scale. + open func scale() -> Int16 { + return _scale + } } extension NSNumber { - public var decimalValue: Decimal { NSUnimplemented() } + public var decimalValue: Decimal { + if let d = self as? NSDecimalNumber { + return d.decimal + } else { + return Decimal(self.doubleValue) + } + } } // Could be silently inexact for float and double. diff --git a/Foundation/NSNumber.swift b/Foundation/NSNumber.swift index 724cc020ad..14bec84edf 100644 --- a/Foundation/NSNumber.swift +++ b/Foundation/NSNumber.swift @@ -290,7 +290,11 @@ open class NSNumber : NSValue { super.init() _CFNumberInitBool(_cfObject, value) } - + + override internal init() { + super.init() + } + public required convenience init(bytes buffer: UnsafeRawPointer, objCType: UnsafePointer) { guard let type = _NSSimpleObjCType(UInt8(objCType.pointee)) else { fatalError("NSNumber.init: unsupported type encoding spec '\(String(cString: objCType))'") diff --git a/TestFoundation/TestNSDecimal.swift b/TestFoundation/TestNSDecimal.swift index 72f33f472f..f28e16ccab 100644 --- a/TestFoundation/TestNSDecimal.swift +++ b/TestFoundation/TestNSDecimal.swift @@ -19,18 +19,92 @@ class TestNSDecimal: XCTestCase { static var allTests : [(String, (TestNSDecimal) -> () throws -> Void)] { return [ + ("test_AdditionWithNormalization", test_AdditionWithNormalization), ("test_BasicConstruction", test_BasicConstruction), ("test_Constants", test_Constants), ("test_Description", test_Description), ("test_ExplicitConstruction", test_ExplicitConstruction), ("test_Maths", test_Maths), ("test_Misc", test_Misc), + ("test_MultiplicationOverflow", test_MultiplicationOverflow), + ("test_NaNInput", test_NaNInput), + ("test_NegativeAndZeroMultiplication", test_NegativeAndZeroMultiplication), ("test_Normalise", test_Normalise), - ("test_Round", test_Round), ("test_NSDecimal", test_NSDecimal), + ("test_PositivePowers", test_PositivePowers), + ("test_RepeatingDivision", test_RepeatingDivision), + ("test_Round", test_Round), + ("test_SimpleMultiplication", test_SimpleMultiplication), + ("test_SmallerNumbers", test_SmallerNumbers), + ("test_ZeroPower", test_ZeroPower), ] } + func test_AdditionWithNormalization() { + + let biggie = Decimal(65536) + let smallee = Decimal(65536) + let answer = biggie/smallee + XCTAssertEqual(Decimal(1),answer) + + var one = Decimal(1) + var addend = Decimal(1) + var expected = Decimal() + var result = Decimal() + + expected._isNegative = 0; + expected._isCompact = 0; + + // 2 digits -- certain to work + addend._exponent = -1; + XCTAssertEqual(.noError, NSDecimalAdd(&result, &one, &addend, .plain), "1 + 0.1") + expected._exponent = -1; + expected._length = 1; + expected._mantissa.0 = 11; + XCTAssertEqual(.orderedSame, NSDecimalCompare(&expected, &result), "1.1 == 1 + 0.1") + + // 38 digits -- guaranteed by NSDecimal to work + addend._exponent = -37; + XCTAssertEqual(.noError, NSDecimalAdd(&result, &one, &addend, .plain), "1 + 1e-37") + expected._exponent = -37; + expected._length = 8; + expected._mantissa.0 = 0x0001; + expected._mantissa.1 = 0x0000; + expected._mantissa.2 = 0x36a0; + expected._mantissa.3 = 0x00f4; + expected._mantissa.4 = 0x46d9; + expected._mantissa.5 = 0xd5da; + expected._mantissa.6 = 0xee10; + expected._mantissa.7 = 0x0785; + XCTAssertEqual(.orderedSame, NSDecimalCompare(&expected, &result), "1 + 1e-37") + + // 39 digits -- not guaranteed to work but it happens to, so we make the test work either way + addend._exponent = -38; + let error = NSDecimalAdd(&result, &one, &addend, .plain) + XCTAssertTrue(error == .noError || error == .lossOfPrecision, "1 + 1e-38") + if error == .noError { + expected._exponent = -38; + expected._length = 8; + expected._mantissa.0 = 0x0001; + expected._mantissa.1 = 0x0000; + expected._mantissa.2 = 0x2240; + expected._mantissa.3 = 0x098a; + expected._mantissa.4 = 0xc47a; + expected._mantissa.5 = 0x5a86; + expected._mantissa.6 = 0x4ca8; + expected._mantissa.7 = 0x4b3b; + XCTAssertEqual(.orderedSame, NSDecimalCompare(&expected, &result), "1 + 1e-38") + } else { + XCTAssertEqual(.orderedSame, NSDecimalCompare(&one, &result), "1 + 1e-38") + } + + // 40 digits -- doesn't work; need to make sure it's rounding for us + addend._exponent = -39; + XCTAssertEqual(.lossOfPrecision, NSDecimalAdd(&result, &one, &addend, .plain), "1 + 1e-39") + XCTAssertEqual("1", result.description) + XCTAssertEqual(.orderedSame, NSDecimalCompare(&one, &result), "1 + 1e-39") + } + func test_BasicConstruction() { let zero = Decimal() XCTAssertEqual(20, MemoryLayout.size) @@ -39,7 +113,7 @@ class TestNSDecimal: XCTestCase { XCTAssertEqual(0, zero._isNegative) XCTAssertEqual(0, zero._isCompact) XCTAssertEqual(0, zero._reserved) - let (m0,m1,m2,m3,m4,m5,m6,m7) = zero._mantissa + let (m0, m1, m2, m3, m4, m5, m6, m7) = zero._mantissa XCTAssertEqual(0, m0) XCTAssertEqual(0, m1) XCTAssertEqual(0, m2) @@ -59,47 +133,47 @@ class TestNSDecimal: XCTestCase { XCTAssertFalse(zero.isSignaling) } func test_Constants() { - XCTAssertEqual(8,NSDecimalMaxSize) - XCTAssertEqual(32767,NSDecimalNoScale) + XCTAssertEqual(8, NSDecimalMaxSize) + XCTAssertEqual(32767, NSDecimalNoScale) let smallest = Decimal(_exponent: 127, _length: 8, _isNegative: 1, _isCompact: 1, _reserved: 0, _mantissa: (UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max)) XCTAssertEqual(smallest, Decimal.leastFiniteMagnitude) let biggest = Decimal(_exponent: 127, _length: 8, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max)) XCTAssertEqual(biggest, Decimal.greatestFiniteMagnitude) - let leastNormal = Decimal(_exponent: -127, _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (1,0,0,0,0,0,0,0)) + let leastNormal = Decimal(_exponent: -127, _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (1, 0, 0, 0, 0, 0, 0, 0)) XCTAssertEqual(leastNormal, Decimal.leastNormalMagnitude) - let leastNonzero = Decimal(_exponent: -127, _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (1,0,0,0,0,0,0,0)) + let leastNonzero = Decimal(_exponent: -127, _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (1, 0, 0, 0, 0, 0, 0, 0)) XCTAssertEqual(leastNonzero, Decimal.leastNonzeroMagnitude) let pi = Decimal(_exponent: -38, _length: 8, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (0x6623, 0x7d57, 0x16e7, 0xad0d, 0xaf52, 0x4641, 0xdfa7, 0xec58)) - XCTAssertEqual(pi,Decimal.pi) - XCTAssertEqual(10,Decimal.radix) + XCTAssertEqual(pi, Decimal.pi) + XCTAssertEqual(10, Decimal.radix) XCTAssertTrue(Decimal().isCanonical) XCTAssertFalse(Decimal().isSignalingNaN) XCTAssertFalse(Decimal.nan.isSignalingNaN) XCTAssertTrue(Decimal.nan.isNaN) - XCTAssertEqual(.quietNaN,Decimal.nan.floatingPointClass) - XCTAssertEqual(.positiveZero,Decimal().floatingPointClass) - XCTAssertEqual(.negativeNormal,smallest.floatingPointClass) - XCTAssertEqual(.positiveNormal,biggest.floatingPointClass) + XCTAssertEqual(.quietNaN, Decimal.nan.floatingPointClass) + XCTAssertEqual(.positiveZero, Decimal().floatingPointClass) + XCTAssertEqual(.negativeNormal, smallest.floatingPointClass) + XCTAssertEqual(.positiveNormal, biggest.floatingPointClass) XCTAssertFalse(Double.nan.isFinite) XCTAssertFalse(Double.nan.isInfinite) } func test_Description() { - XCTAssertEqual("0",Decimal().description) - XCTAssertEqual("0",Decimal(0).description) - XCTAssertEqual("10",Decimal(_exponent: 1, _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (1,0,0,0,0,0,0,0)).description) - XCTAssertEqual("10",Decimal(10).description) - XCTAssertEqual("123.458",Decimal(_exponent: -3, _length: 2, _isNegative: 0, _isCompact:1, _reserved: 0, _mantissa: (57922,1,0,0,0,0,0,0)).description) - XCTAssertEqual("123.458",Decimal(123.458).description) - XCTAssertEqual("123",Decimal(UInt8(123)).description) - XCTAssertEqual("45",Decimal(Int8(45)).description) - XCTAssertEqual("3.14159265358979323846264338327950288419",Decimal.pi.description) - XCTAssertEqual("-30000000000",Decimal(sign: .minus, exponent: 10, significand: Decimal(3)).description) - XCTAssertEqual("300000",Decimal(sign: .plus, exponent: 5, significand: Decimal(3)).description) - XCTAssertEqual("5",Decimal(signOf: Decimal(3), magnitudeOf: Decimal(5)).description) - XCTAssertEqual("-5",Decimal(signOf: Decimal(-3), magnitudeOf: Decimal(5)).description) - XCTAssertEqual("5",Decimal(signOf: Decimal(3), magnitudeOf: Decimal(-5)).description) - XCTAssertEqual("-5",Decimal(signOf: Decimal(-3), magnitudeOf: Decimal(-5)).description) + XCTAssertEqual("0", Decimal().description) + XCTAssertEqual("0", Decimal(0).description) + XCTAssertEqual("10", Decimal(_exponent: 1, _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (1, 0, 0, 0, 0, 0, 0, 0)).description) + XCTAssertEqual("10", Decimal(10).description) + XCTAssertEqual("123.458", Decimal(_exponent: -3, _length: 2, _isNegative: 0, _isCompact:1, _reserved: 0, _mantissa: (57922, 1, 0, 0, 0, 0, 0, 0)).description) + XCTAssertEqual("123.458", Decimal(123.458).description) + XCTAssertEqual("123", Decimal(UInt8(123)).description) + XCTAssertEqual("45", Decimal(Int8(45)).description) + XCTAssertEqual("3.14159265358979323846264338327950288419", Decimal.pi.description) + XCTAssertEqual("-30000000000", Decimal(sign: .minus, exponent: 10, significand: Decimal(3)).description) + XCTAssertEqual("300000", Decimal(sign: .plus, exponent: 5, significand: Decimal(3)).description) + XCTAssertEqual("5", Decimal(signOf: Decimal(3), magnitudeOf: Decimal(5)).description) + XCTAssertEqual("-5", Decimal(signOf: Decimal(-3), magnitudeOf: Decimal(5)).description) + XCTAssertEqual("5", Decimal(signOf: Decimal(3), magnitudeOf: Decimal(-5)).description) + XCTAssertEqual("-5", Decimal(signOf: Decimal(-3), magnitudeOf: Decimal(-5)).description) } func test_ExplicitConstruction() { @@ -109,7 +183,7 @@ class TestNSDecimal: XCTestCase { _isNegative: 3, _isCompact: 4, _reserved: UInt32(1<<18 + 1<<17 + 1), - _mantissa: (6,7,8,9,10,11,12,13) + _mantissa: (6, 7, 8, 9, 10, 11, 12, 13) ) XCTAssertEqual(0x7f, explicit._exponent) XCTAssertEqual(0x7f, explicit.exponent) @@ -119,7 +193,7 @@ class TestNSDecimal: XCTestCase { XCTAssertTrue(explicit.isSignMinus) XCTAssertEqual(0, explicit._isCompact) XCTAssertEqual(UInt32(1<<17 + 1), explicit._reserved) - let (m0,m1,m2,m3,m4,m5,m6,m7) = explicit._mantissa + let (m0, m1, m2, m3, m4, m5, m6, m7) = explicit._mantissa XCTAssertEqual(6, m0) XCTAssertEqual(7, m1) XCTAssertEqual(8, m2) @@ -143,7 +217,7 @@ class TestNSDecimal: XCTestCase { XCTAssertEqual(0, significand._isNegative) XCTAssertEqual(1, significand._isCompact) XCTAssertEqual(0, significand._reserved) - let (sm0,sm1,sm2,sm3,sm4,sm5,sm6,sm7) = significand._mantissa + let (sm0, sm1, sm2, sm3, sm4, sm5, sm6, sm7) = significand._mantissa XCTAssertEqual(6, sm0) XCTAssertEqual(7, sm1) XCTAssertEqual(8, sm2) @@ -204,14 +278,14 @@ class TestNSDecimal: XCTestCase { } func test_Misc() { - XCTAssertEqual(.minus,Decimal(-5.2).sign) - XCTAssertEqual(.plus,Decimal(5.2).sign) + XCTAssertEqual(.minus, Decimal(-5.2).sign) + XCTAssertEqual(.plus, Decimal(5.2).sign) var d = Decimal(5.2) - XCTAssertEqual(.plus,d.sign) + XCTAssertEqual(.plus, d.sign) d.negate() - XCTAssertEqual(.minus,d.sign) + XCTAssertEqual(.minus, d.sign) d.negate() - XCTAssertEqual(.plus,d.sign) + XCTAssertEqual(.plus, d.sign) XCTAssertTrue(Decimal(3.5).isEqual(to: Decimal(3.5))) XCTAssertTrue(Decimal.nan.isEqual(to: Decimal.nan)) XCTAssertTrue(Decimal(1.28).isLess(than: Decimal(2.24))) @@ -228,46 +302,226 @@ class TestNSDecimal: XCTestCase { XCTAssertFalse(Decimal.nan.isTotallyOrdered(belowOrEqualTo: Decimal(2.3))) XCTAssertTrue(Decimal(2) < Decimal(3)) XCTAssertTrue(Decimal(3) > Decimal(2)) - XCTAssertEqual(3275573729074,Decimal(1234).hashValue) + XCTAssertEqual(3275573729074, Decimal(1234).hashValue) XCTAssertEqual(Decimal(-9), Decimal(1) - Decimal(10)) - XCTAssertEqual(Decimal(3),Decimal(2).nextUp) - XCTAssertEqual(Decimal(2),Decimal(3).nextDown) - XCTAssertEqual(Decimal(-476),Decimal(1024).distance(to: Decimal(1500))) - XCTAssertEqual(Decimal(68040),Decimal(386).advanced(by: Decimal(67654))) - XCTAssertEqual(Decimal(1.234),abs(Decimal(1.234))) - XCTAssertEqual(Decimal(1.234),abs(Decimal(-1.234))) + XCTAssertEqual(Decimal(3), Decimal(2).nextUp) + XCTAssertEqual(Decimal(2), Decimal(3).nextDown) + XCTAssertEqual(Decimal(-476), Decimal(1024).distance(to: Decimal(1500))) + XCTAssertEqual(Decimal(68040), Decimal(386).advanced(by: Decimal(67654))) + XCTAssertEqual(Decimal(1.234), abs(Decimal(1.234))) + XCTAssertEqual(Decimal(1.234), abs(Decimal(-1.234))) var a = Decimal(1234) - XCTAssertEqual(.noError,NSDecimalMultiplyByPowerOf10(&a,&a,1,.plain)) - XCTAssertEqual(Decimal(12340),a) + XCTAssertEqual(.noError, NSDecimalMultiplyByPowerOf10(&a, &a, 1, .plain)) + XCTAssertEqual(Decimal(12340), a) a = Decimal(1234) - XCTAssertEqual(.noError,NSDecimalMultiplyByPowerOf10(&a,&a,2,.plain)) - XCTAssertEqual(Decimal(123400),a) - XCTAssertEqual(.overflow,NSDecimalMultiplyByPowerOf10(&a,&a,128,.plain)) + XCTAssertEqual(.noError, NSDecimalMultiplyByPowerOf10(&a, &a, 2, .plain)) + XCTAssertEqual(Decimal(123400), a) + XCTAssertEqual(.overflow, NSDecimalMultiplyByPowerOf10(&a, &a, 128, .plain)) XCTAssertTrue(a.isNaN) a = Decimal(1234) - XCTAssertEqual(.noError,NSDecimalMultiplyByPowerOf10(&a,&a,-2,.plain)) - XCTAssertEqual(Decimal(12.34),a) - XCTAssertEqual(.underflow,NSDecimalMultiplyByPowerOf10(&a,&a,-128,.plain)) + XCTAssertEqual(.noError, NSDecimalMultiplyByPowerOf10(&a, &a, -2, .plain)) + XCTAssertEqual(Decimal(12.34), a) + XCTAssertEqual(.underflow, NSDecimalMultiplyByPowerOf10(&a, &a, -128, .plain)) XCTAssertTrue(a.isNaN) a = Decimal(1234) - XCTAssertEqual(.noError,NSDecimalPower(&a,&a,0,.plain)) - XCTAssertEqual(Decimal(1),a) + XCTAssertEqual(.noError, NSDecimalPower(&a, &a, 0, .plain)) + XCTAssertEqual(Decimal(1), a) a = Decimal(8) - XCTAssertEqual(.noError,NSDecimalPower(&a,&a,2,.plain)) - XCTAssertEqual(Decimal(64),a) + XCTAssertEqual(.noError, NSDecimalPower(&a, &a, 2, .plain)) + XCTAssertEqual(Decimal(64), a) a = Decimal(-2) - XCTAssertEqual(.noError,NSDecimalPower(&a,&a,3,.plain)) - XCTAssertEqual(Decimal(-8),a) + XCTAssertEqual(.noError, NSDecimalPower(&a, &a, 3, .plain)) + XCTAssertEqual(Decimal(-8), a) for i in -2...10 { for j in 0...5 { var actual = Decimal(i) - XCTAssertEqual(.noError,NSDecimalPower(&actual,&actual,j,.plain)) - let expected = Decimal(pow(Double(i),Double(j))) + XCTAssertEqual(.noError, NSDecimalPower(&actual, &actual, j, .plain)) + let expected = Decimal(pow(Double(i), Double(j))) XCTAssertEqual(expected, actual, "\(actual) == \(i)^\(j)") } } } + func test_MultiplicationOverflow() { + var multiplicand = Decimal(_exponent: 0, _length: 8, _isNegative: 0, _isCompact: 0, _reserved: 0, _mantissa: ( 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff )) + + var result = Decimal() + var multiplier = Decimal(1) + + multiplier._mantissa.0 = 2 + + XCTAssertEqual(.noError, NSDecimalMultiply(&result, &multiplicand, &multiplier, .plain), "2 * max mantissa") + XCTAssertEqual(.noError, NSDecimalMultiply(&result, &multiplier, &multiplicand, .plain), "max mantissa * 2") + + multiplier._exponent = 0x7f + XCTAssertEqual(.overflow, NSDecimalMultiply(&result, &multiplicand, &multiplier, .plain), "2e127 * max mantissa") + XCTAssertEqual(.overflow, NSDecimalMultiply(&result, &multiplier, &multiplicand, .plain), "max mantissa * 2e127") + } + + func test_NaNInput() { + var NaN = Decimal.nan + var one = Decimal(1) + var result = Decimal() + + XCTAssertNotEqual(.noError, NSDecimalAdd(&result, &NaN, &one, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "NaN + 1") + XCTAssertNotEqual(.noError, NSDecimalAdd(&result, &one, &NaN, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "1 + NaN") + + XCTAssertNotEqual(.noError, NSDecimalSubtract(&result, &NaN, &one, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "NaN - 1") + XCTAssertNotEqual(.noError, NSDecimalSubtract(&result, &one, &NaN, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "1 - NaN") + + XCTAssertNotEqual(.noError, NSDecimalMultiply(&result, &NaN, &one, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "NaN * 1") + XCTAssertNotEqual(.noError, NSDecimalMultiply(&result, &one, &NaN, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "1 * NaN") + + XCTAssertNotEqual(.noError, NSDecimalDivide(&result, &NaN, &one, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "NaN / 1") + XCTAssertNotEqual(.noError, NSDecimalDivide(&result, &one, &NaN, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "1 / NaN") + + XCTAssertNotEqual(.noError, NSDecimalPower(&result, &NaN, 0, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "NaN ^ 0") + XCTAssertNotEqual(.noError, NSDecimalPower(&result, &NaN, 4, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "NaN ^ 4") + XCTAssertNotEqual(.noError, NSDecimalPower(&result, &NaN, 5, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "NaN ^ 5") + + XCTAssertNotEqual(.noError, NSDecimalMultiplyByPowerOf10(&result, &NaN, 0, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "NaN e0") + XCTAssertNotEqual(.noError, NSDecimalMultiplyByPowerOf10(&result, &NaN, 4, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "NaN e4") + XCTAssertNotEqual(.noError, NSDecimalMultiplyByPowerOf10(&result, &NaN, 5, .plain)) + XCTAssertTrue(NSDecimalIsNotANumber(&result), "NaN e5") + } + + func test_NegativeAndZeroMultiplication() { + var one = Decimal(1) + var zero = Decimal(0) + var negativeOne = Decimal(-1) + + var result = Decimal() + + XCTAssertEqual(.noError, NSDecimalMultiply(&result, &one, &one, .plain), "1 * 1") + XCTAssertEqual(.orderedSame, NSDecimalCompare(&one, &result), "1 * 1") + + XCTAssertEqual(.noError, NSDecimalMultiply(&result, &one, &negativeOne, .plain), "1 * -1") + XCTAssertEqual(.orderedSame, NSDecimalCompare(&negativeOne, &result), "1 * -1") + + XCTAssertEqual(.noError, NSDecimalMultiply(&result, &negativeOne, &one, .plain), "-1 * 1") + XCTAssertEqual(.orderedSame, NSDecimalCompare(&negativeOne, &result), "-1 * 1") + + XCTAssertEqual(.noError, NSDecimalMultiply(&result, &negativeOne, &negativeOne, .plain), "-1 * -1") + XCTAssertEqual(.orderedSame, NSDecimalCompare(&one, &result), "-1 * -1") + + XCTAssertEqual(.noError, NSDecimalMultiply(&result, &one, &zero, .plain), "1 * 0") + XCTAssertEqual(.orderedSame, NSDecimalCompare(&zero, &result), "1 * 0") + XCTAssertEqual(0, result._isNegative, "1 * 0") + + XCTAssertEqual(.noError, NSDecimalMultiply(&result, &zero, &one, .plain), "0 * 1") + XCTAssertEqual(.orderedSame, NSDecimalCompare(&zero, &result), "0 * 1") + XCTAssertEqual(0, result._isNegative, "0 * 1") + + XCTAssertEqual(.noError, NSDecimalMultiply(&result, &negativeOne, &zero, .plain), "-1 * 0") + XCTAssertEqual(.orderedSame, NSDecimalCompare(&zero, &result), "-1 * 0") + XCTAssertEqual(0, result._isNegative, "-1 * 0") + + XCTAssertEqual(.noError, NSDecimalMultiply(&result, &zero, &negativeOne, .plain), "0 * -1") + XCTAssertEqual(.orderedSame, NSDecimalCompare(&zero, &result), "0 * -1") + XCTAssertEqual(0, result._isNegative, "0 * -1") + } + + func test_Normalise() { + var one = Decimal(1) + var ten = Decimal(-10) + XCTAssertEqual(.noError, NSDecimalNormalize(&one, &ten, .plain)) + XCTAssertEqual(Decimal(1), one) + XCTAssertEqual(Decimal(-10), ten) + XCTAssertEqual(1, one._length) + XCTAssertEqual(1, ten._length) + one = Decimal(1) + ten = Decimal(10) + XCTAssertEqual(.noError, NSDecimalNormalize(&one, &ten, .plain)) + XCTAssertEqual(Decimal(1), one) + XCTAssertEqual(Decimal(10), ten) + XCTAssertEqual(1, one._length) + XCTAssertEqual(1, ten._length) + } + + func test_NSDecimal() { + var nan = Decimal.nan + XCTAssertTrue(NSDecimalIsNotANumber(&nan)) + var zero = Decimal() + XCTAssertFalse(NSDecimalIsNotANumber(&zero)) + var three = Decimal(3) + var guess = Decimal() + NSDecimalCopy(&guess, &three) + XCTAssertEqual(three, guess) + + var f = Decimal(_exponent: 0, _length: 2, _isNegative: 0, _isCompact: 0, _reserved: 0, _mantissa: (0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) + let before = f.description + XCTAssertEqual(0, f._isCompact) + NSDecimalCompact(&f) + XCTAssertEqual(1, f._isCompact) + let after = f.description + XCTAssertEqual(before, after) + } + + func test_PositivePowers() { + let six = NSDecimalNumber(integerLiteral: 6) + + XCTAssertEqual(6, six.raising(toPower:1).intValue) + XCTAssertEqual(36, six.raising(toPower:2).intValue) + XCTAssertEqual(216, six.raising(toPower:3).intValue) + XCTAssertEqual(1296, six.raising(toPower:4).intValue) + XCTAssertEqual(7776, six.raising(toPower:5).intValue) + XCTAssertEqual(46656, six.raising(toPower:6).intValue) + XCTAssertEqual(279936, six.raising(toPower:7).intValue) + XCTAssertEqual(1679616, six.raising(toPower:8).intValue) + XCTAssertEqual(10077696, six.raising(toPower:9).intValue) + + let negativeSix = NSDecimalNumber(integerLiteral: -6) + + XCTAssertEqual(-6, negativeSix.raising(toPower:1).intValue) + XCTAssertEqual(36, negativeSix.raising(toPower:2).intValue) + XCTAssertEqual(-216, negativeSix.raising(toPower:3).intValue) + XCTAssertEqual(1296, negativeSix.raising(toPower:4).intValue) + XCTAssertEqual(-7776, negativeSix.raising(toPower:5).intValue) + XCTAssertEqual(46656, negativeSix.raising(toPower:6).intValue) + XCTAssertEqual(-279936, negativeSix.raising(toPower:7).intValue) + XCTAssertEqual(1679616, negativeSix.raising(toPower:8).intValue) + XCTAssertEqual(-10077696, negativeSix.raising(toPower:9).intValue) + } + + func test_RepeatingDivision() { + let repeatingNumerator = Decimal(16) + let repeatingDenominator = Decimal(9) + let repeating = repeatingNumerator / repeatingDenominator + + let numerator = Decimal(1010) + var result = numerator / repeating + + var expected = Decimal() + expected._exponent = -35; + expected._length = 8; + expected._isNegative = 0; + expected._isCompact = 1; + expected._reserved = 0; + expected._mantissa.0 = 51946; + expected._mantissa.1 = 3; + expected._mantissa.2 = 15549; + expected._mantissa.3 = 55864; + expected._mantissa.4 = 57984; + expected._mantissa.5 = 55436; + expected._mantissa.6 = 45186; + expected._mantissa.7 = 10941; + + XCTAssertEqual(.orderedSame, NSDecimalCompare(&expected, &result), "568.12500000000000000000000000000248554: \(expected.description) != \(result.description)"); + } + func test_Round() { let testCases = [ // expected, start, scale, round @@ -290,48 +544,63 @@ class TestNSDecimal: XCTestCase { ( -5.5, -5.5, 1, Decimal.RoundingMode.up ), ( -6.5, -6.5, 1, Decimal.RoundingMode.plain ), ( -7.5, -7.5, 1, Decimal.RoundingMode.bankers ), - ] + ] for testCase in testCases { let (expected, start, scale, mode) = testCase var num = Decimal(start) - NSDecimalRound(&num,&num,scale,mode) + NSDecimalRound(&num, &num, scale, mode) XCTAssertEqual(Decimal(expected), num) } } - func test_Normalise() { - var one = Decimal(1) - var ten = Decimal(-10) - XCTAssertEqual(.noError,NSDecimalNormalize(&one,&ten,.plain)) - XCTAssertEqual(Decimal(1),one) - XCTAssertEqual(Decimal(-10),ten) - XCTAssertEqual(1,one._length) - XCTAssertEqual(1,ten._length) - one = Decimal(1) - ten = Decimal(10) - XCTAssertEqual(.noError,NSDecimalNormalize(&one,&ten,.plain)) - XCTAssertEqual(Decimal(1),one) - XCTAssertEqual(Decimal(10),ten) - XCTAssertEqual(1,one._length) - XCTAssertEqual(1,ten._length) + func test_SimpleMultiplication() { + var multiplicand = Decimal() + multiplicand._isNegative = 0 + multiplicand._isCompact = 0 + multiplicand._length = 1 + multiplicand._exponent = 1 + + var multiplier = multiplicand + multiplier._exponent = 2 + + var expected = multiplicand + expected._isNegative = 0 + expected._isCompact = 0 + expected._exponent = 3 + expected._length = 1 + + var result = Decimal() + + for i in 1..