From 2d3c2eec5506695b9cae5f88a56ea95529bfbea6 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Tue, 29 Dec 2020 01:12:35 +0000 Subject: [PATCH] Decimal: init?(string:) and scanDecimal() should return nil on error. - If the exponent is too large for Int8 then return nil instead of NaN. - This matches Darwin's behaviour. --- Sources/Foundation/Decimal.swift | 8 +++--- Tests/Foundation/Tests/TestDecimal.swift | 31 ++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Sources/Foundation/Decimal.swift b/Sources/Foundation/Decimal.swift index 6d4d50fad4..2960f0fcb1 100644 --- a/Sources/Foundation/Decimal.swift +++ b/Sources/Foundation/Decimal.swift @@ -2295,7 +2295,7 @@ extension Scanner { repeat { buf.advance() } while decimalValue(buf.currentCharacter) != nil - return Decimal.nan + return nil } result._exponent += 1 } @@ -2315,7 +2315,7 @@ extension Scanner { repeat { buf.advance() } while decimalValue(buf.currentCharacter) != nil - return Decimal.nan + return nil } result._exponent -= 1 } @@ -2339,7 +2339,7 @@ extension Scanner { while let numeral = decimalValue(buf.currentCharacter) { exponent = 10 * exponent + Int32(numeral) guard exponent <= 2*Int32(Int8.max) else { - return Decimal.nan + return nil } buf.advance() @@ -2350,7 +2350,7 @@ extension Scanner { } exponent += result._exponent guard exponent >= Int32(Int8.min) && exponent <= Int32(Int8.max) else { - return Decimal.nan + return nil } result._exponent = exponent } diff --git a/Tests/Foundation/Tests/TestDecimal.swift b/Tests/Foundation/Tests/TestDecimal.swift index 590d7a99db..8bef77b21e 100644 --- a/Tests/Foundation/Tests/TestDecimal.swift +++ b/Tests/Foundation/Tests/TestDecimal.swift @@ -749,7 +749,7 @@ class TestDecimal: XCTestCase { } } - func test_ScanDecimal() { + func test_ScanDecimal() throws { let testCases = [ // expected, value ( 123.456e78, "123.456e78" ), @@ -764,7 +764,7 @@ class TestDecimal: XCTestCase { ] for testCase in testCases { let (expected, string) = testCase - let decimal = Decimal(string:string)! + let decimal = try XCTUnwrap(Decimal(string:string)) let aboutOne = Decimal(expected) / decimal let approximatelyRight = aboutOne >= Decimal(0.99999) && aboutOne <= Decimal(1.00001) XCTAssertTrue(approximatelyRight, "\(expected) ~= \(decimal) : \(aboutOne) \(aboutOne >= Decimal(0.99999)) \(aboutOne <= Decimal(1.00001))" ) @@ -779,6 +779,33 @@ class TestDecimal: XCTestCase { return } XCTAssertEqual(answer,num,"\(ones) / 9 = \(answer) \(num)") + + // Exponent overflow, returns nil + XCTAssertNil(Decimal(string: "1e200")) + XCTAssertNil(Decimal(string: "1e-200")) + XCTAssertNil(Decimal(string: "1e300")) + XCTAssertNil(Decimal(string: "1" + String(repeating: "0", count: 170))) + XCTAssertNil(Decimal(string: "0." + String(repeating: "0", count: 170) + "1")) + XCTAssertNil(Decimal(string: "0e200")) + + // Parsing zero in different forms + let zero1 = try XCTUnwrap(Decimal(string: "000.000e123")) + XCTAssertTrue(zero1.isZero) + XCTAssertEqual(zero1._isNegative, 0) + XCTAssertEqual(zero1._length, 0) + XCTAssertEqual(zero1.description, "0") + + let zero2 = try XCTUnwrap(Decimal(string: "+000.000e-123")) + XCTAssertTrue(zero2.isZero) + XCTAssertEqual(zero2._isNegative, 0) + XCTAssertEqual(zero2._length, 0) + XCTAssertEqual(zero2.description, "0") + + let zero3 = try XCTUnwrap(Decimal(string: "-0.0e1")) + XCTAssertTrue(zero3.isZero) + XCTAssertEqual(zero3._isNegative, 0) + XCTAssertEqual(zero3._length, 0) + XCTAssertEqual(zero3.description, "0") } func test_SimpleMultiplication() {