Skip to content

Commit 6f98bcd

Browse files
committed
Decimal: NSDecimalNormalize() - avoid overflow/underflow on exponent difference
- The difference of the expoents could exceed Int8.min / Int8.max, so convert to Int first. - This avoids a crash if comparing numbers with a large exponent difference.
1 parent d782c4f commit 6f98bcd

File tree

2 files changed

+25
-4
lines changed

2 files changed

+25
-4
lines changed

Diff for: Foundation/Decimal.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,7 @@ public func NSDecimalRound(_ result: UnsafeMutablePointer<Decimal>, _ number: Un
11051105
// scale indicates number of significant digits after the decimal point
11061106

11071107
public func NSDecimalNormalize(_ a: UnsafeMutablePointer<Decimal>, _ b: UnsafeMutablePointer<Decimal>, _ roundingMode: NSDecimalNumber.RoundingMode) -> NSDecimalNumber.CalculationError {
1108-
var diffexp = a.pointee.__exponent - b.pointee.__exponent
1108+
var diffexp = Int(a.pointee.__exponent) - Int(b.pointee.__exponent)
11091109
var result = Decimal()
11101110

11111111
//
@@ -1142,7 +1142,7 @@ public func NSDecimalNormalize(_ a: UnsafeMutablePointer<Decimal>, _ b: UnsafeMu
11421142
// Try to multiply aa to reach the same exponent level than bb
11431143
//
11441144

1145-
if integerMultiplyByPowerOf10(&result, aa.pointee, Int(diffexp)) == .noError {
1145+
if integerMultiplyByPowerOf10(&result, aa.pointee, diffexp) == .noError {
11461146
// Succeed. Adjust the length/exponent info
11471147
// and return no errorNSDecimalNormalize
11481148
aa.pointee.copyMantissa(from: result)
@@ -1165,10 +1165,10 @@ public func NSDecimalNormalize(_ a: UnsafeMutablePointer<Decimal>, _ b: UnsafeMu
11651165
//
11661166
// Divide bb by this value
11671167
//
1168-
_ = integerMultiplyByPowerOf10(&result, bb.pointee, Int(maxpow10 - diffexp))
1168+
_ = integerMultiplyByPowerOf10(&result, bb.pointee, Int(maxpow10) - diffexp)
11691169

11701170
bb.pointee.copyMantissa(from: result)
1171-
bb.pointee._exponent -= Int32(maxpow10 - diffexp);
1171+
bb.pointee._exponent -= (Int32(maxpow10) - Int32(diffexp))
11721172

11731173
//
11741174
// If bb > 0 multiply aa by the same value

Diff for: TestFoundation/TestDecimal.swift

+21
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,27 @@ class TestDecimal: XCTestCase {
517517
XCTAssertEqual(Decimal(10), ten)
518518
XCTAssertEqual(1, one._length)
519519
XCTAssertEqual(1, ten._length)
520+
521+
// Check equality with numbers with large exponent difference
522+
var small = Decimal.leastNonzeroMagnitude
523+
var large = Decimal.greatestFiniteMagnitude
524+
XCTAssertTrue(Int(large.exponent) - Int(small.exponent) > Int(Int8.max))
525+
XCTAssertTrue(Int(small.exponent) - Int(large.exponent) < Int(Int8.min))
526+
XCTAssertNotEqual(small, large)
527+
528+
XCTAssertEqual(small.exponent, -127)
529+
XCTAssertEqual(large.exponent, 127)
530+
XCTAssertEqual(.lossOfPrecision, NSDecimalNormalize(&small, &large, .plain))
531+
XCTAssertEqual(small.exponent, 127)
532+
XCTAssertEqual(large.exponent, 127)
533+
534+
small = Decimal.leastNonzeroMagnitude
535+
large = Decimal.greatestFiniteMagnitude
536+
XCTAssertEqual(small.exponent, -127)
537+
XCTAssertEqual(large.exponent, 127)
538+
XCTAssertEqual(.lossOfPrecision, NSDecimalNormalize(&large, &small, .plain))
539+
XCTAssertEqual(small.exponent, 127)
540+
XCTAssertEqual(large.exponent, 127)
520541
}
521542

522543
func test_NSDecimal() throws {

0 commit comments

Comments
 (0)