From 81fddf2757afec0f18920eefb5cb4e4cfd14ad77 Mon Sep 17 00:00:00 2001 From: Butta Date: Fri, 19 Jun 2020 20:23:57 +0530 Subject: [PATCH] SR-13015: Decimal calculation incorrect on Linux Fix typo and logic error in the overflow check in integerMultiply(), which was screwing up Decimal calculations under certain conditions. --- Sources/Foundation/Decimal.swift | 2 +- Tests/Foundation/Tests/TestDecimal.swift | 51 +++++++++++++++++++++++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/Sources/Foundation/Decimal.swift b/Sources/Foundation/Decimal.swift index 3cc82545bb..a465b42bc5 100644 --- a/Sources/Foundation/Decimal.swift +++ b/Sources/Foundation/Decimal.swift @@ -1025,7 +1025,7 @@ fileprivate func integerMultiply(_ big: inout T, accumulator = UInt32(carry) + bigij + UInt32(right[j]) * UInt32(left[i]) carry = UInt16(truncatingIfNeeded:accumulator >> 16) big[i+j] = UInt16(truncatingIfNeeded:accumulator) - } else if carry != 0 || (right[j] == 0 && left[j] == 0) { + } else if carry != 0 || (right[j] > 0 && left[i] > 0) { return .overflow } } diff --git a/Tests/Foundation/Tests/TestDecimal.swift b/Tests/Foundation/Tests/TestDecimal.swift index 783e7d5f8e..cd7cc58c21 100644 --- a/Tests/Foundation/Tests/TestDecimal.swift +++ b/Tests/Foundation/Tests/TestDecimal.swift @@ -342,7 +342,7 @@ class TestDecimal: XCTestCase { XCTAssertEqual(otherBits + Decimal(1), highBit) } - func test_Misc() { + func test_Misc() throws { XCTAssertEqual(.minus, Decimal(-5.2).sign) XCTAssertEqual(.plus, Decimal(5.2).sign) var d = Decimal(5.2) @@ -426,6 +426,23 @@ class TestDecimal: XCTestCase { XCTAssertEqual(expected, pow(power, j)) } } + + do { + // SR-13015 + let a = try XCTUnwrap(Decimal(string: "119.993")) + let b = try XCTUnwrap(Decimal(string: "4.1565")) + let c = try XCTUnwrap(Decimal(string: "18.209")) + let d = try XCTUnwrap(Decimal(string: "258.469")) + let ab = a * b + let aDivD = a / d + let caDivD = c * aDivD + XCTAssertEqual(ab, try XCTUnwrap(Decimal(string: "498.7509045"))) + XCTAssertEqual(aDivD, try XCTUnwrap(Decimal(string: "0.46424522863476857959755328492004843907"))) + XCTAssertEqual(caDivD, try XCTUnwrap(Decimal(string: "8.453441368210501065891847765109162027"))) + + let result = (a * b) + (c * (a / d)) + XCTAssertEqual(result, try XCTUnwrap(Decimal(string: "507.2043458682105010658918477651091"))) + } } func test_MultiplicationOverflow() { @@ -522,7 +539,7 @@ class TestDecimal: XCTestCase { XCTAssertEqual(0, result._isNegative, "0 * -1") } - func test_Normalise() { + func test_Normalise() throws { var one = Decimal(1) var ten = Decimal(-10) XCTAssertEqual(.noError, NSDecimalNormalize(&one, &ten, .plain)) @@ -558,6 +575,36 @@ class TestDecimal: XCTestCase { XCTAssertEqual(.lossOfPrecision, NSDecimalNormalize(&large, &small, .plain)) XCTAssertEqual(small.exponent, 127) XCTAssertEqual(large.exponent, 127) + + // Normalise with loss of precision + let a = try XCTUnwrap(Decimal(string: "498.7509045")) + let b = try XCTUnwrap(Decimal(string: "8.453441368210501065891847765109162027")) + + var aNormalized = a + var bNormalized = b + let normalizeError = NSDecimalNormalize(&aNormalized, &bNormalized, .plain) + XCTAssertEqual(normalizeError, NSDecimalNumber.CalculationError.lossOfPrecision) + + XCTAssertEqual(aNormalized.exponent, -31) + XCTAssertEqual(aNormalized._mantissa.0, 0) + XCTAssertEqual(aNormalized._mantissa.1, 21760) + XCTAssertEqual(aNormalized._mantissa.2, 45355) + XCTAssertEqual(aNormalized._mantissa.3, 11455) + XCTAssertEqual(aNormalized._mantissa.4, 62709) + XCTAssertEqual(aNormalized._mantissa.5, 14050) + XCTAssertEqual(aNormalized._mantissa.6, 62951) + XCTAssertEqual(aNormalized._mantissa.7, 0) + XCTAssertEqual(bNormalized.exponent, -31) + XCTAssertEqual(bNormalized._mantissa.0, 56467) + XCTAssertEqual(bNormalized._mantissa.1, 17616) + XCTAssertEqual(bNormalized._mantissa.2, 59987) + XCTAssertEqual(bNormalized._mantissa.3, 21635) + XCTAssertEqual(bNormalized._mantissa.4, 5988) + XCTAssertEqual(bNormalized._mantissa.5, 63852) + XCTAssertEqual(bNormalized._mantissa.6, 1066) + XCTAssertEqual(bNormalized._mantissa.7, 1628) + XCTAssertEqual(a, aNormalized) + XCTAssertNotEqual(b, bNormalized) // b had a loss Of Precision when normalising } func test_NSDecimal() throws {