Skip to content

Commit 114343a

Browse files
authored
improve Decimal conversion performance using _mantissa (#124)
* improve Decimal conversion performance using `_mantissa` * fix linux builds by keeping the decimal string init in a compiler directive * more efficient zero out assurance after buffer creation * Decimal (from bigint) initializer
1 parent 97a80fb commit 114343a

File tree

4 files changed

+76
-1
lines changed

4 files changed

+76
-1
lines changed

Sources/Data Conversion.swift

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

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

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

@@ -69,6 +68,8 @@ extension BigUInt {
6968
i -= 1
7069
}
7170
}
71+
let zeroOut = UnsafeMutableBufferPointer<UInt8>(start: buffer.baseAddress, count: i)
72+
zeroOut.initialize(repeating: 0)
7273
return UnsafeRawBufferPointer(start: buffer.baseAddress, count: byteCount)
7374
}
7475

Sources/Floating Point Conversion.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,20 @@ extension BigUInt {
4949
guard integer.sign == .plus else { return nil }
5050
assert(integer.floatingPointClass == .positiveNormal)
5151

52+
#if os(Linux)
53+
// `Decimal._mantissa` has an internal access level on linux, and it might get
54+
// deprecated in the future, so keeping the string implementation around for now.
5255
let significand = BigUInt("\(integer.significand)")!
56+
#else
57+
let significand = {
58+
var start = BigUInt(0)
59+
for (place, value) in integer.significand.mantissaParts.enumerated() {
60+
guard value > 0 else { continue }
61+
start += (1 << (place * 16)) * BigUInt(value)
62+
}
63+
return start
64+
}()
65+
#endif
5366
let exponent = BigUInt(10).power(integer.exponent)
5467

5568
self = significand * exponent
@@ -124,3 +137,45 @@ extension BigInt.Sign {
124137
}
125138
}
126139
}
140+
141+
#if canImport(Foundation)
142+
public extension Decimal {
143+
init(_ value: BigUInt) {
144+
guard
145+
value < BigUInt(exactly: Decimal.greatestFiniteMagnitude)!
146+
else {
147+
self = .greatestFiniteMagnitude
148+
return
149+
}
150+
guard !value.isZero else { self = 0; return }
151+
152+
self.init(string: "\(value)")!
153+
}
154+
155+
init(_ value: BigInt) {
156+
if value >= 0 {
157+
self.init(BigUInt(value))
158+
} else {
159+
self.init(value.magnitude)
160+
self *= -1
161+
}
162+
}
163+
}
164+
#endif
165+
166+
#if canImport(Foundation) && !os(Linux)
167+
private extension Decimal {
168+
var mantissaParts: [UInt16] {
169+
[
170+
_mantissa.0,
171+
_mantissa.1,
172+
_mantissa.2,
173+
_mantissa.3,
174+
_mantissa.4,
175+
_mantissa.5,
176+
_mantissa.6,
177+
_mantissa.7,
178+
]
179+
}
180+
}
181+
#endif

Tests/BigIntTests/BigIntTests.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,18 @@ class BigIntTests: XCTestCase {
185185
test(BigInt(1) << 1024, Float.infinity)
186186
test(BigInt(words: convertWords([0, 0xFFFFFF0000000000, 0])),
187187
Float.greatestFiniteMagnitude)
188+
189+
XCTAssertEqual(Decimal(BigInt(0)), 0)
190+
XCTAssertEqual(Decimal(BigInt(20)), 20)
191+
XCTAssertEqual(Decimal(BigInt(123456789)), 123456789)
192+
XCTAssertEqual(Decimal(BigInt(exactly: Decimal.greatestFiniteMagnitude)!), .greatestFiniteMagnitude)
193+
XCTAssertEqual(Decimal(BigInt(exactly: Decimal.greatestFiniteMagnitude)! * 2), .greatestFiniteMagnitude)
194+
XCTAssertEqual(Decimal(-BigInt(0)), 0)
195+
XCTAssertEqual(Decimal(-BigInt(20)), -20)
196+
XCTAssertEqual(Decimal(-BigInt(123456789)), -123456789)
197+
XCTAssertEqual(Decimal(-BigInt(exactly: Decimal.greatestFiniteMagnitude)!), -.greatestFiniteMagnitude)
198+
XCTAssertEqual(Decimal(-BigInt(exactly: Decimal.greatestFiniteMagnitude)! * 2), -.greatestFiniteMagnitude)
199+
188200
}
189201

190202
func testTwosComplement() {

Tests/BigIntTests/BigUIntTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ class BigUIntTests: XCTestCase {
264264
test(BigUInt(0x8000027FFFFFFFFF as UInt64), 0x800002p40 as Float)
265265
test(BigUInt(0x8000028000000000 as UInt64), 0x800002p40 as Float)
266266
test(BigUInt(0x800002FFFFFFFFFF as UInt64), 0x800002p40 as Float)
267+
268+
XCTAssertEqual(Decimal(BigUInt(0)), 0)
269+
XCTAssertEqual(Decimal(BigUInt(20)), 20)
270+
XCTAssertEqual(Decimal(BigUInt(123456789)), 123456789)
271+
XCTAssertEqual(Decimal(BigUInt(exactly: Decimal.greatestFiniteMagnitude)!), .greatestFiniteMagnitude)
272+
XCTAssertEqual(Decimal(BigUInt(exactly: Decimal.greatestFiniteMagnitude)! * 2), .greatestFiniteMagnitude)
267273
}
268274

269275
func testInit_Misc() {
@@ -688,6 +694,7 @@ class BigUIntTests: XCTestCase {
688694
test(BigUInt(), [])
689695
test(BigUInt(1), [0x01])
690696
test(BigUInt(2), [0x02])
697+
test(BigUInt(0x010203), [0x1, 0x2, 0x3])
691698
test(BigUInt(0x0102030405060708), [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08])
692699
test(BigUInt(0x01) << 64 + BigUInt(0x0203040506070809), [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09])
693700
}

0 commit comments

Comments
 (0)