Skip to content

Commit 116e15e

Browse files
authored
For bin compat: add special handling to Decimal string parsing for (#768)
- Decimal(string: "en") -> 0 - Decimal(string: "e") -> 0 - Decimal(string: "123e") -> 123 This keeps the same parsing behavior as NSDecimal
1 parent acae3d2 commit 116e15e

File tree

2 files changed

+24
-2
lines changed

2 files changed

+24
-2
lines changed

Sources/FoundationEssentials/Decimal/Decimal.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ extension Decimal {
355355
result = product
356356
}
357357
// Get the decimal point
358-
if index != utf8View.endIndex && stringViewContainsDecimalSeparator(at: index) {
358+
if index < utf8View.endIndex && stringViewContainsDecimalSeparator(at: index) {
359359
utf8View.formIndex(&index, offsetBy: decimalSeparator.count)
360360
// Continue to build the mantissa
361361
while index != utf8View.endIndex,
@@ -381,8 +381,21 @@ extension Decimal {
381381
}
382382
}
383383
// Get the exponent if any
384-
if index != utf8View.endIndex && (utf8View[index] == UInt8._E || utf8View[index] == UInt8._e) {
384+
if index < utf8View.endIndex && (utf8View[index] == UInt8._E || utf8View[index] == UInt8._e) {
385385
utf8View.formIndex(after: &index)
386+
// If there is no content after e, the string is invalid
387+
guard index != utf8View.endIndex else {
388+
// Normally we should return .parseFailure
389+
// However, NSDecimal historically parses any
390+
// - Invalid strings starting with `e` as 0
391+
// - "en" -> 0
392+
// - "e" -> 0
393+
// - Strings ending with `e` but nothing after as valid
394+
// - "1234e" -> 1234
395+
// So let's keep that behavior here as well
396+
let processedLength = utf8View.distance(from: utf8View.startIndex, to: index)
397+
return .success(result, processedLength: processedLength)
398+
}
386399
var exponentIsNegative = false
387400
var exponent = 0
388401
// Get the exponent sign

Tests/FoundationEssentialsTests/DecimalTests.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,15 @@ final class DecimalTests : XCTestCase {
260260
XCTAssertEqual(zero3._isNegative, 0)
261261
XCTAssertEqual(zero3._length, 0)
262262
XCTAssertEqual(zero3.description, "0")
263+
264+
// Bin compat: invalid strings starting with E should be parsed as 0
265+
var zeroE = try XCTUnwrap(Decimal(string: "en"))
266+
XCTAssertTrue(zeroE.isZero)
267+
zeroE = try XCTUnwrap(Decimal(string: "e"))
268+
XCTAssertTrue(zeroE.isZero)
269+
// Partitally valid strings ending with e shold be parsed
270+
let notZero = try XCTUnwrap(Decimal(string: "123e"))
271+
XCTAssertEqual(notZero, Decimal(123))
263272
}
264273

265274
func test_stringWithLocale() {

0 commit comments

Comments
 (0)