Skip to content

Commit 4e41c33

Browse files
committed
Use NSDecimalString(_:_) for Decimal -> String conversion.
- This allows using an optional Locale to localise the output. - Update Decimal.description, NSDecimalNumber.description and NSDecimalNumber.description(withLocale) to call NSDecimalString().
1 parent d643057 commit 4e41c33

File tree

3 files changed

+160
-93
lines changed

3 files changed

+160
-93
lines changed

Foundation/Decimal.swift

Lines changed: 68 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -267,61 +267,9 @@ extension Decimal : CustomStringConvertible {
267267
self = theDecimal
268268
}
269269

270-
// Note: In the Darwin overlay, `description` is implemented in terms of
271-
// `NSDecimalString(_:_:)`; here, it's the other way around.
272270
public var description: String {
273-
if self.isNaN {
274-
return "NaN"
275-
}
276-
if _length == 0 {
277-
return "0"
278-
}
279-
var copy = self
280-
let ZERO: CChar = 0x30 // ASCII '0' == 0x30
281-
let DECIMALPOINT: CChar = 0x2e // ASCII '.' == 0x2e
282-
let MINUS: CChar = 0x2d // ASCII '-' == 0x2d
283-
284-
let bufferSize = 200 // Max value: 39 + 128 + sign + decimal point
285-
var buffer = Array<CChar>(repeating: 0, count: bufferSize)
286-
287-
var i = bufferSize - 1
288-
while copy._exponent > 0 {
289-
i -= 1
290-
buffer[i] = ZERO
291-
copy._exponent -= 1
292-
}
293-
294-
if copy._exponent == 0 {
295-
copy._exponent = 1
296-
}
297-
298-
while copy._length != 0 {
299-
var remainder: UInt16 = 0
300-
if copy._exponent == 0 {
301-
i -= 1
302-
buffer[i] = DECIMALPOINT
303-
}
304-
copy._exponent += 1
305-
(remainder, _) = divideByShort(&copy, 10)
306-
i -= 1
307-
buffer[i] = Int8(remainder) + ZERO
308-
}
309-
if copy._exponent <= 0 {
310-
while copy._exponent != 0 {
311-
i -= 1
312-
buffer[i] = ZERO
313-
copy._exponent += 1
314-
}
315-
i -= 1
316-
buffer[i] = DECIMALPOINT
317-
i -= 1
318-
buffer[i] = ZERO
319-
}
320-
if copy._isNegative != 0 {
321-
i -= 1
322-
buffer[i] = MINUS
323-
}
324-
return String(cString: Array(buffer.suffix(from: i)))
271+
var value = self
272+
return NSDecimalString(&value, nil)
325273
}
326274
}
327275

@@ -1691,10 +1639,73 @@ public func NSDecimalMultiplyByPowerOf10(_ result: UnsafeMutablePointer<Decimal>
16911639
}
16921640

16931641
public func NSDecimalString(_ dcm: UnsafePointer<Decimal>, _ locale: Any?) -> String {
1694-
guard locale == nil else {
1695-
fatalError("Locale not supported: \(locale!)")
1642+
1643+
var copy = dcm.pointee
1644+
if copy.isNaN {
1645+
return "NaN"
1646+
}
1647+
if copy._length == 0 {
1648+
return "0"
1649+
}
1650+
1651+
let ZERO: CChar = 0x30 // ASCII '0' == 0x30
1652+
let DECIMALPOINT: CChar = 0x2e // ASCII '.' == 0x2e
1653+
let MINUS: CChar = 0x2d // ASCII '-' == 0x2d
1654+
let bufferSize = 200 // Max value: 39 + 128 + sign + decimal point
1655+
var buffer = Array<CChar>(repeating: 0, count: bufferSize)
1656+
1657+
var i = bufferSize - 1
1658+
while copy._exponent > 0 {
1659+
i -= 1
1660+
buffer[i] = ZERO
1661+
copy._exponent -= 1
1662+
}
1663+
1664+
if copy._exponent == 0 {
1665+
copy._exponent = 1
16961666
}
1697-
return dcm.pointee.description
1667+
1668+
while copy._length != 0 {
1669+
var remainder: UInt16 = 0
1670+
if copy._exponent == 0 {
1671+
i -= 1
1672+
buffer[i] = DECIMALPOINT
1673+
}
1674+
copy._exponent += 1
1675+
(remainder, _) = divideByShort(&copy, 10)
1676+
i -= 1
1677+
buffer[i] = Int8(remainder) + ZERO
1678+
}
1679+
if copy._exponent <= 0 {
1680+
while copy._exponent != 0 {
1681+
i -= 1
1682+
buffer[i] = ZERO
1683+
copy._exponent += 1
1684+
}
1685+
i -= 1
1686+
buffer[i] = DECIMALPOINT
1687+
i -= 1
1688+
buffer[i] = ZERO
1689+
}
1690+
if copy._isNegative != 0 {
1691+
i -= 1
1692+
buffer[i] = MINUS
1693+
}
1694+
let result = String(cString: Array(buffer.suffix(from: i)))
1695+
1696+
var decimalSeparator: String?
1697+
if let locale = locale as? Locale {
1698+
decimalSeparator = locale.decimalSeparator
1699+
} else if let dictionary = locale as? [NSLocale.Key: String] {
1700+
decimalSeparator = dictionary[NSLocale.Key.decimalSeparator]
1701+
} else if let dictionary = locale as? [String: String] {
1702+
decimalSeparator = dictionary[NSLocale.Key.decimalSeparator.rawValue]
1703+
}
1704+
1705+
if let dcm = decimalSeparator, dcm != "." {
1706+
return result.replacingOccurrences(of: ".", with: dcm)
1707+
}
1708+
return result
16981709
}
16991710

17001711
private func multiplyBy10(_ dcm: inout Decimal, andAdd extra:Int) -> NSDecimalNumber.CalculationError {

Foundation/NSDecimalNumber.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,14 +206,13 @@ open class NSDecimalNumber : NSNumber {
206206
}
207207

208208
open override var description: String {
209-
return self.decimal.description
209+
var number = self.decimal
210+
return NSDecimalString(&number, nil)
210211
}
211212

212213
open override func description(withLocale locale: Locale?) -> String {
213-
guard locale == nil else {
214-
fatalError("Locale not supported: \(locale!)")
215-
}
216-
return self.decimal.description
214+
var number = self.decimal
215+
return NSDecimalString(&number, locale)
217216
}
218217

219218
open class var zero: NSDecimalNumber {

TestFoundation/TestDecimal.swift

Lines changed: 88 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,6 @@
99

1010
class TestDecimal: XCTestCase {
1111

12-
static var allTests : [(String, (TestDecimal) -> () throws -> Void)] {
13-
return [
14-
("test_NSDecimalNumberInit", test_NSDecimalNumberInit),
15-
("test_AdditionWithNormalization", test_AdditionWithNormalization),
16-
("test_BasicConstruction", test_BasicConstruction),
17-
("test_Constants", test_Constants),
18-
("test_Description", test_Description),
19-
("test_ExplicitConstruction", test_ExplicitConstruction),
20-
("test_Maths", test_Maths),
21-
("test_Misc", test_Misc),
22-
("test_MultiplicationOverflow", test_MultiplicationOverflow),
23-
("test_NaNInput", test_NaNInput),
24-
("test_NegativeAndZeroMultiplication", test_NegativeAndZeroMultiplication),
25-
("test_Normalise", test_Normalise),
26-
("test_NSDecimal", test_NSDecimal),
27-
("test_PositivePowers", test_PositivePowers),
28-
("test_RepeatingDivision", test_RepeatingDivision),
29-
("test_Round", test_Round),
30-
("test_ScanDecimal", test_ScanDecimal),
31-
("test_SimpleMultiplication", test_SimpleMultiplication),
32-
("test_SmallerNumbers", test_SmallerNumbers),
33-
("test_ZeroPower", test_ZeroPower),
34-
("test_doubleValue", test_doubleValue),
35-
("test_NSDecimalNumberValues", test_NSDecimalNumberValues),
36-
("test_bridging", test_bridging),
37-
("test_stringWithLocale", test_stringWithLocale),
38-
]
39-
}
40-
4112
func test_NSDecimalNumberInit() {
4213
XCTAssertEqual(NSDecimalNumber(mantissa: 123456789000, exponent: -2, isNegative: true), -1234567890)
4314
XCTAssertEqual(NSDecimalNumber(decimal: Decimal()).decimalValue, Decimal(0))
@@ -203,8 +174,43 @@ class TestDecimal: XCTestCase {
203174
XCTAssertEqual("-5", Decimal(signOf: Decimal(-3), magnitudeOf: Decimal(5)).description)
204175
XCTAssertEqual("5", Decimal(signOf: Decimal(3), magnitudeOf: Decimal(-5)).description)
205176
XCTAssertEqual("-5", Decimal(signOf: Decimal(-3), magnitudeOf: Decimal(-5)).description)
206-
XCTAssertEqual("5", NSDecimalNumber(decimal:Decimal(5)).description)
207-
XCTAssertEqual("-5", NSDecimalNumber(decimal:Decimal(-5)).description)
177+
XCTAssertEqual("5", NSDecimalNumber(decimal: Decimal(5)).description)
178+
XCTAssertEqual("-5", NSDecimalNumber(decimal: Decimal(-5)).description)
179+
XCTAssertEqual("-3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", Decimal.leastFiniteMagnitude.description)
180+
XCTAssertEqual("3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", Decimal.greatestFiniteMagnitude.description)
181+
XCTAssertEqual("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", Decimal.leastNormalMagnitude.description)
182+
XCTAssertEqual("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", Decimal.leastNonzeroMagnitude.description)
183+
184+
let fr = Locale(identifier: "fr_FR")
185+
let leastFiniteMagnitude = "-3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
186+
let greatestFiniteMagnitude = "3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
187+
188+
XCTAssertEqual("0", NSDecimalNumber(decimal: Decimal()).description(withLocale: fr))
189+
XCTAssertEqual("1000", NSDecimalNumber(decimal: Decimal(1000)).description(withLocale: fr))
190+
XCTAssertEqual("10", NSDecimalNumber(decimal: Decimal(10)).description(withLocale: fr))
191+
XCTAssertEqual("123,458", NSDecimalNumber(decimal: Decimal(123.458)).description(withLocale: fr))
192+
XCTAssertEqual("123", NSDecimalNumber(decimal: Decimal(UInt8(123))).description(withLocale: fr))
193+
XCTAssertEqual("3,14159265358979323846264338327950288419", NSDecimalNumber(decimal: Decimal.pi).description(withLocale: fr))
194+
XCTAssertEqual("-30000000000", NSDecimalNumber(decimal: Decimal(sign: .minus, exponent: 10, significand: Decimal(3))).description(withLocale: fr))
195+
XCTAssertEqual("123456,789", NSDecimalNumber(decimal: Decimal(string: "123456.789")!).description(withLocale: fr))
196+
XCTAssertEqual(leastFiniteMagnitude, NSDecimalNumber(decimal: Decimal.leastFiniteMagnitude).description(withLocale: fr))
197+
XCTAssertEqual(greatestFiniteMagnitude, NSDecimalNumber(decimal: Decimal.greatestFiniteMagnitude).description(withLocale: fr))
198+
XCTAssertEqual("0,0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", NSDecimalNumber(decimal: Decimal.leastNormalMagnitude).description(withLocale: fr))
199+
XCTAssertEqual("0,0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", NSDecimalNumber(decimal: Decimal.leastNonzeroMagnitude).description(withLocale: fr))
200+
201+
let en = Locale(identifier: "en_GB")
202+
XCTAssertEqual("0", NSDecimalNumber(decimal: Decimal()).description(withLocale: en))
203+
XCTAssertEqual("1000", NSDecimalNumber(decimal: Decimal(1000)).description(withLocale: en))
204+
XCTAssertEqual("10", NSDecimalNumber(decimal: Decimal(10)).description(withLocale: en))
205+
XCTAssertEqual("123.458", NSDecimalNumber(decimal: Decimal(123.458)).description(withLocale: en))
206+
XCTAssertEqual("123", NSDecimalNumber(decimal: Decimal(UInt8(123))).description(withLocale: en))
207+
XCTAssertEqual("3.14159265358979323846264338327950288419", NSDecimalNumber(decimal: Decimal.pi).description(withLocale: en))
208+
XCTAssertEqual("-30000000000", NSDecimalNumber(decimal: Decimal(sign: .minus, exponent: 10, significand: Decimal(3))).description(withLocale: en))
209+
XCTAssertEqual("123456.789", NSDecimalNumber(decimal: Decimal(string: "123456.789")!).description(withLocale: en))
210+
XCTAssertEqual(leastFiniteMagnitude, NSDecimalNumber(decimal: Decimal.leastFiniteMagnitude).description(withLocale: en))
211+
XCTAssertEqual(greatestFiniteMagnitude, NSDecimalNumber(decimal: Decimal.greatestFiniteMagnitude).description(withLocale: en))
212+
XCTAssertEqual("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", NSDecimalNumber(decimal: Decimal.leastNormalMagnitude).description(withLocale: en))
213+
XCTAssertEqual("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", NSDecimalNumber(decimal: Decimal.leastNonzeroMagnitude).description(withLocale: en))
208214
}
209215

210216
func test_ExplicitConstruction() {
@@ -1116,4 +1122,55 @@ class TestDecimal: XCTestCase {
11161122
XCTAssertEqual(Decimal(string: s2, locale: en_US)?.description, "1234")
11171123
XCTAssertEqual(Decimal(string: s2, locale: fr_FR)?.description, s1)
11181124
}
1125+
1126+
func test_NSDecimalString() {
1127+
var decimal = Decimal(string: "-123456.789")!
1128+
XCTAssertEqual(NSDecimalString(&decimal, nil), "-123456.789")
1129+
let en = NSDecimalString(&decimal, Locale(identifier: "en_GB"))
1130+
XCTAssertEqual(en, "-123456.789")
1131+
let fr = NSDecimalString(&decimal, Locale(identifier: "fr_FR"))
1132+
XCTAssertEqual(fr, "-123456,789")
1133+
1134+
let d1: [NSLocale.Key: String] = [ .decimalSeparator: "@@@"]
1135+
XCTAssertEqual(NSDecimalString(&decimal, d1), "-123456@@@789")
1136+
let d2: [NSLocale.Key: String] = [ .decimalSeparator: "()"]
1137+
XCTAssertEqual(NSDecimalString(&decimal, NSDictionary(dictionary: d2)), "-123456()789")
1138+
let d3: [String: String] = [ "kCFLocaleDecimalSeparatorKey": "X"]
1139+
XCTAssertEqual(NSDecimalString(&decimal, NSDictionary(dictionary: d3)), "-123456X789")
1140+
1141+
// Input is ignored
1142+
let d4: [Int: String] = [123: "X"]
1143+
XCTAssertEqual(NSDecimalString(&decimal, NSDictionary(dictionary: d4)), "-123456.789")
1144+
}
1145+
1146+
1147+
static var allTests : [(String, (TestDecimal) -> () throws -> Void)] {
1148+
return [
1149+
("test_NSDecimalNumberInit", test_NSDecimalNumberInit),
1150+
("test_AdditionWithNormalization", test_AdditionWithNormalization),
1151+
("test_BasicConstruction", test_BasicConstruction),
1152+
("test_Constants", test_Constants),
1153+
("test_Description", test_Description),
1154+
("test_ExplicitConstruction", test_ExplicitConstruction),
1155+
("test_Maths", test_Maths),
1156+
("test_Misc", test_Misc),
1157+
("test_MultiplicationOverflow", test_MultiplicationOverflow),
1158+
("test_NaNInput", test_NaNInput),
1159+
("test_NegativeAndZeroMultiplication", test_NegativeAndZeroMultiplication),
1160+
("test_Normalise", test_Normalise),
1161+
("test_NSDecimal", test_NSDecimal),
1162+
("test_PositivePowers", test_PositivePowers),
1163+
("test_RepeatingDivision", test_RepeatingDivision),
1164+
("test_Round", test_Round),
1165+
("test_ScanDecimal", test_ScanDecimal),
1166+
("test_SimpleMultiplication", test_SimpleMultiplication),
1167+
("test_SmallerNumbers", test_SmallerNumbers),
1168+
("test_ZeroPower", test_ZeroPower),
1169+
("test_doubleValue", test_doubleValue),
1170+
("test_NSDecimalNumberValues", test_NSDecimalNumberValues),
1171+
("test_bridging", test_bridging),
1172+
("test_stringWithLocale", test_stringWithLocale),
1173+
("test_NSDecimalString", test_NSDecimalString),
1174+
]
1175+
}
11191176
}

0 commit comments

Comments
 (0)