Skip to content

Commit 97a80fb

Browse files
authored
Decimal init performance (#123)
* add extreme Decimal tests * fix Decimal init performance * assure buffer is empty before using
1 parent a7ee114 commit 97a80fb

File tree

4 files changed

+27
-28
lines changed

4 files changed

+27
-28
lines changed

Sources/Data Conversion.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ extension BigUInt {
5353
let byteCount = (self.bitWidth + 7) / 8
5454

5555
let buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: byteCount)
56+
buffer.initialize(repeating: 0)
5657

5758
guard byteCount > 0 else { return UnsafeRawBufferPointer(start: buffer.baseAddress, count: 0) }
5859

Sources/Floating Point Conversion.swift

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,38 +29,30 @@ extension BigUInt {
2929

3030
#if canImport(Foundation)
3131
public init?(exactly source: Decimal) {
32-
guard source.isFinite else { return nil }
33-
guard !source.isZero else { self = 0; return }
34-
guard source.sign == .plus else { return nil }
35-
assert(source.floatingPointClass == .positiveNormal)
3632
guard source.exponent >= 0 else { return nil }
37-
let intMaxD = Decimal(UInt.max)
38-
let intMaxB = BigUInt(UInt.max)
39-
var start = BigUInt()
40-
var value = source
41-
while value >= intMaxD {
42-
start += intMaxB
43-
value -= intMaxD
44-
}
45-
start += BigUInt((value as NSNumber).uintValue)
46-
self = start
33+
self.init(commonDecimal: source)
4734
}
4835

4936
public init?(truncating source: Decimal) {
50-
guard source.isFinite else { return nil }
51-
guard !source.isZero else { self = 0; return }
52-
guard source.sign == .plus else { return nil }
53-
assert(source.floatingPointClass == .positiveNormal)
54-
let intMaxD = Decimal(UInt.max)
55-
let intMaxB = BigUInt(UInt.max)
56-
var start = BigUInt()
57-
var value = source
58-
while value >= intMaxD {
59-
start += intMaxB
60-
value -= intMaxD
37+
self.init(commonDecimal: source)
38+
}
39+
40+
private init?(commonDecimal source: Decimal) {
41+
var integer = source
42+
if source.exponent < 0 {
43+
var source = source
44+
NSDecimalRound(&integer, &source, 0, .down)
6145
}
62-
start += BigUInt((value as NSNumber).uintValue)
63-
self = start
46+
47+
guard !integer.isZero else { self = 0; return }
48+
guard integer.isFinite else { return nil }
49+
guard integer.sign == .plus else { return nil }
50+
assert(integer.floatingPointClass == .positiveNormal)
51+
52+
let significand = BigUInt("\(integer.significand)")!
53+
let exponent = BigUInt(10).power(integer.exponent)
54+
55+
self = significand * exponent
6456
}
6557
#endif
6658
}

Tests/BigIntTests/BigIntTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class BigIntTests: XCTestCase {
102102
XCTAssertEqual(BigInt(exactly: Decimal(1001.5)), nil)
103103
XCTAssertEqual(BigInt(exactly: Decimal(UInt.max) + 5), "18446744073709551620")
104104
XCTAssertEqual(BigInt(exactly: (Decimal(UInt.max) + 5.5)), nil)
105+
XCTAssertEqual(BigInt(exactly: Decimal.greatestFiniteMagnitude),
106+
"3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
105107
XCTAssertEqual(BigInt(truncating: Decimal(0)), 0)
106108
XCTAssertEqual(BigInt(truncating: Decimal(Double.nan)), nil)
107109
XCTAssertEqual(BigInt(truncating: Decimal(10)), 10)
@@ -119,6 +121,8 @@ class BigIntTests: XCTestCase {
119121
XCTAssertEqual(BigInt(exactly: -Decimal(1001.5)), nil)
120122
XCTAssertEqual(BigInt(exactly: -(Decimal(UInt.max) + 5)), "-18446744073709551620")
121123
XCTAssertEqual(BigInt(exactly: -(Decimal(UInt.max) + 5.5)), nil)
124+
XCTAssertEqual(BigInt(exactly: Decimal.leastFiniteMagnitude),
125+
"-3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
122126
XCTAssertEqual(BigInt(truncating: -Decimal(10)), -10)
123127
XCTAssertEqual(BigInt(truncating: -Decimal(1000)), -1000)
124128
XCTAssertEqual(BigInt(truncating: -Decimal(1000.1)), -1000)

Tests/BigIntTests/BigUIntTests.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ class BigUIntTests: XCTestCase {
167167
XCTAssertEqual(BigUInt(exactly: Decimal(1001.5)), nil)
168168
XCTAssertEqual(BigUInt(exactly: Decimal(UInt.max) + 5), "18446744073709551620")
169169
XCTAssertEqual(BigUInt(exactly: (Decimal(UInt.max) + 5.5)), nil)
170+
XCTAssertEqual(BigUInt(exactly: Decimal.greatestFiniteMagnitude),
171+
"3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
170172
XCTAssertEqual(BigUInt(truncating: Decimal(0)), 0)
171173
XCTAssertEqual(BigUInt(truncating: Decimal(Double.nan)), nil)
172174
XCTAssertEqual(BigUInt(truncating: Decimal(10)), 10)
@@ -184,6 +186,7 @@ class BigUIntTests: XCTestCase {
184186
XCTAssertEqual(BigUInt(exactly: -Decimal(1001.5)), nil)
185187
XCTAssertEqual(BigUInt(exactly: -Decimal(UInt.max) + 5), nil)
186188
XCTAssertEqual(BigUInt(exactly: -(Decimal(UInt.max) + 5.5)), nil)
189+
XCTAssertEqual(BigUInt(exactly: Decimal.leastFiniteMagnitude), nil)
187190
XCTAssertEqual(BigUInt(truncating: -Decimal(10)), nil)
188191
XCTAssertEqual(BigUInt(truncating: -Decimal(1000)), nil)
189192
XCTAssertEqual(BigUInt(truncating: -Decimal(1000.1)), nil)
@@ -1513,5 +1516,4 @@ class BigUIntTests: XCTestCase {
15131516
let limit = BigUInt(UInt64.max) * BigUInt(UInt64.max) * BigUInt(UInt64.max)
15141517
check { BigUInt.randomInteger(lessThan: limit, using: &$0) }
15151518
}
1516-
15171519
}

0 commit comments

Comments
 (0)