diff --git a/Sources/Foundation/Decimal.swift b/Sources/Foundation/Decimal.swift index 37ca9ff541..9bfa452b55 100644 --- a/Sources/Foundation/Decimal.swift +++ b/Sources/Foundation/Decimal.swift @@ -1801,7 +1801,7 @@ public func NSDecimalPower(_ result: UnsafeMutablePointer, _ number: Un return .overflow } NSDecimalCopy(result,number) - return result.pointee.power(UInt(power), roundingMode:roundingMode) + return result.pointee.power(power, roundingMode:roundingMode) } public func NSDecimalMultiplyByPowerOf10(_ result: UnsafeMutablePointer, _ number: UnsafePointer, _ power: Int16, _ roundingMode: NSDecimalNumber.RoundingMode) -> NSDecimalNumber.CalculationError { @@ -2242,27 +2242,27 @@ extension Decimal { _exponent = newExponent return .noError } - fileprivate mutating func power(_ p:UInt, roundingMode:RoundingMode) -> CalculationError { + fileprivate mutating func power(_ p:Int, roundingMode:RoundingMode) -> CalculationError { if isNaN { return .overflow } - var power = p - if power == 0 { + if p == 0 { _exponent = 0 _length = 1 _isNegative = 0 self[0] = 1 _isCompact = 1 return .noError - } else if power == 1 { + } else if p == 1 || isZero { return .noError } + var absPower = abs(p) var temporary = Decimal(1) var error:CalculationError = .noError - while power > 1 { - if power % 2 == 1 { + while absPower > 1 { + if absPower % 2 == 1 { let previousError = error var leftOp = temporary error = NSDecimalMultiply(&temporary, &leftOp, &self, roundingMode) @@ -2275,9 +2275,9 @@ extension Decimal { setNaN() return error } - power -= 1 + absPower -= 1 } - if power != 0 { + if absPower != 0 { let previousError = error var leftOp = self var rightOp = self @@ -2291,13 +2291,26 @@ extension Decimal { setNaN() return error } - power /= 2 + absPower /= 2 } } let previousError = error var rightOp = self error = NSDecimalMultiply(&self, &temporary, &rightOp, roundingMode) - + + // if power is negative, use multiplicative inverse + if p < 0 { + var leftOp = Decimal(1) + var rightOp = self + + error = NSDecimalDivide( + &self, + &leftOp, + &rightOp, + roundingMode + ) + } + if previousError != .noError { // FIXME is this the intent? error = previousError } diff --git a/Tests/Foundation/Tests/TestDecimal.swift b/Tests/Foundation/Tests/TestDecimal.swift index eaf48a2f64..81b1207c67 100644 --- a/Tests/Foundation/Tests/TestDecimal.swift +++ b/Tests/Foundation/Tests/TestDecimal.swift @@ -670,6 +670,32 @@ class TestDecimal: XCTestCase { XCTAssertEqual(1679616, negativeSix.raising(toPower:8).intValue) XCTAssertEqual(-10077696, negativeSix.raising(toPower:9).intValue) } + + func test_NegativePowers() { + let six = NSDecimalNumber(integerLiteral: 6) + + XCTAssertEqual(1/6 as NSDecimalNumber, six.raising(toPower: -1)) + XCTAssertEqual(1/36 as NSDecimalNumber, six.raising(toPower: -2)) + XCTAssertEqual(1/216 as NSDecimalNumber, six.raising(toPower: -3)) + XCTAssertEqual(1/1296 as NSDecimalNumber, six.raising(toPower: -4)) + XCTAssertEqual(1/7776 as NSDecimalNumber, six.raising(toPower: -5)) + XCTAssertEqual(1/46656 as NSDecimalNumber, six.raising(toPower: -6)) + XCTAssertEqual(1/279936 as NSDecimalNumber, six.raising(toPower: -7)) + XCTAssertEqual(1/1679616 as NSDecimalNumber, six.raising(toPower: -8)) + XCTAssertEqual(1/10077696 as NSDecimalNumber, six.raising(toPower: -9)) + + let negativeSix = NSDecimalNumber(integerLiteral: -6) + + XCTAssertEqual(-1/6 as NSDecimalNumber, negativeSix.raising(toPower: -1)) + XCTAssertEqual(1/36 as NSDecimalNumber, negativeSix.raising(toPower: -2)) + XCTAssertEqual(-1/216 as NSDecimalNumber, negativeSix.raising(toPower: -3)) + XCTAssertEqual(1/1296 as NSDecimalNumber, negativeSix.raising(toPower: -4)) + XCTAssertEqual(-1/7776 as NSDecimalNumber, negativeSix.raising(toPower: -5)) + XCTAssertEqual(1/46656 as NSDecimalNumber, negativeSix.raising(toPower: -6)) + XCTAssertEqual(-1/279936 as NSDecimalNumber, negativeSix.raising(toPower: -7)) + XCTAssertEqual(1/1679616 as NSDecimalNumber, negativeSix.raising(toPower: -8)) + XCTAssertEqual(-1/10077696 as NSDecimalNumber, negativeSix.raising(toPower: -9)) + } func test_RepeatingDivision() { let repeatingNumerator = Decimal(16) @@ -1546,6 +1572,7 @@ class TestDecimal: XCTestCase { ("test_Normalise", test_Normalise), ("test_NSDecimal", test_NSDecimal), ("test_PositivePowers", test_PositivePowers), + ("test_NegativePowers", test_NegativePowers), ("test_RepeatingDivision", test_RepeatingDivision), ("test_Round", test_Round), ("test_ScanDecimal", test_ScanDecimal),