Skip to content

Commit

Permalink
Decimal init performance (#123)
Browse files Browse the repository at this point in the history
* add extreme Decimal tests

* fix Decimal init performance

* assure buffer is empty before using
  • Loading branch information
mredig authored Nov 7, 2024
1 parent a7ee114 commit 97a80fb
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 28 deletions.
1 change: 1 addition & 0 deletions Sources/Data Conversion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ extension BigUInt {
let byteCount = (self.bitWidth + 7) / 8

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

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

Expand Down
46 changes: 19 additions & 27 deletions Sources/Floating Point Conversion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,38 +29,30 @@ extension BigUInt {

#if canImport(Foundation)
public init?(exactly source: Decimal) {
guard source.isFinite else { return nil }
guard !source.isZero else { self = 0; return }
guard source.sign == .plus else { return nil }
assert(source.floatingPointClass == .positiveNormal)
guard source.exponent >= 0 else { return nil }
let intMaxD = Decimal(UInt.max)
let intMaxB = BigUInt(UInt.max)
var start = BigUInt()
var value = source
while value >= intMaxD {
start += intMaxB
value -= intMaxD
}
start += BigUInt((value as NSNumber).uintValue)
self = start
self.init(commonDecimal: source)
}

public init?(truncating source: Decimal) {
guard source.isFinite else { return nil }
guard !source.isZero else { self = 0; return }
guard source.sign == .plus else { return nil }
assert(source.floatingPointClass == .positiveNormal)
let intMaxD = Decimal(UInt.max)
let intMaxB = BigUInt(UInt.max)
var start = BigUInt()
var value = source
while value >= intMaxD {
start += intMaxB
value -= intMaxD
self.init(commonDecimal: source)
}

private init?(commonDecimal source: Decimal) {
var integer = source
if source.exponent < 0 {
var source = source
NSDecimalRound(&integer, &source, 0, .down)
}
start += BigUInt((value as NSNumber).uintValue)
self = start

guard !integer.isZero else { self = 0; return }
guard integer.isFinite else { return nil }
guard integer.sign == .plus else { return nil }
assert(integer.floatingPointClass == .positiveNormal)

let significand = BigUInt("\(integer.significand)")!
let exponent = BigUInt(10).power(integer.exponent)

self = significand * exponent
}
#endif
}
Expand Down
4 changes: 4 additions & 0 deletions Tests/BigIntTests/BigIntTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ class BigIntTests: XCTestCase {
XCTAssertEqual(BigInt(exactly: Decimal(1001.5)), nil)
XCTAssertEqual(BigInt(exactly: Decimal(UInt.max) + 5), "18446744073709551620")
XCTAssertEqual(BigInt(exactly: (Decimal(UInt.max) + 5.5)), nil)
XCTAssertEqual(BigInt(exactly: Decimal.greatestFiniteMagnitude),
"3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
XCTAssertEqual(BigInt(truncating: Decimal(0)), 0)
XCTAssertEqual(BigInt(truncating: Decimal(Double.nan)), nil)
XCTAssertEqual(BigInt(truncating: Decimal(10)), 10)
Expand All @@ -119,6 +121,8 @@ class BigIntTests: XCTestCase {
XCTAssertEqual(BigInt(exactly: -Decimal(1001.5)), nil)
XCTAssertEqual(BigInt(exactly: -(Decimal(UInt.max) + 5)), "-18446744073709551620")
XCTAssertEqual(BigInt(exactly: -(Decimal(UInt.max) + 5.5)), nil)
XCTAssertEqual(BigInt(exactly: Decimal.leastFiniteMagnitude),
"-3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
XCTAssertEqual(BigInt(truncating: -Decimal(10)), -10)
XCTAssertEqual(BigInt(truncating: -Decimal(1000)), -1000)
XCTAssertEqual(BigInt(truncating: -Decimal(1000.1)), -1000)
Expand Down
4 changes: 3 additions & 1 deletion Tests/BigIntTests/BigUIntTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ class BigUIntTests: XCTestCase {
XCTAssertEqual(BigUInt(exactly: Decimal(1001.5)), nil)
XCTAssertEqual(BigUInt(exactly: Decimal(UInt.max) + 5), "18446744073709551620")
XCTAssertEqual(BigUInt(exactly: (Decimal(UInt.max) + 5.5)), nil)
XCTAssertEqual(BigUInt(exactly: Decimal.greatestFiniteMagnitude),
"3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
XCTAssertEqual(BigUInt(truncating: Decimal(0)), 0)
XCTAssertEqual(BigUInt(truncating: Decimal(Double.nan)), nil)
XCTAssertEqual(BigUInt(truncating: Decimal(10)), 10)
Expand All @@ -184,6 +186,7 @@ class BigUIntTests: XCTestCase {
XCTAssertEqual(BigUInt(exactly: -Decimal(1001.5)), nil)
XCTAssertEqual(BigUInt(exactly: -Decimal(UInt.max) + 5), nil)
XCTAssertEqual(BigUInt(exactly: -(Decimal(UInt.max) + 5.5)), nil)
XCTAssertEqual(BigUInt(exactly: Decimal.leastFiniteMagnitude), nil)
XCTAssertEqual(BigUInt(truncating: -Decimal(10)), nil)
XCTAssertEqual(BigUInt(truncating: -Decimal(1000)), nil)
XCTAssertEqual(BigUInt(truncating: -Decimal(1000.1)), nil)
Expand Down Expand Up @@ -1513,5 +1516,4 @@ class BigUIntTests: XCTestCase {
let limit = BigUInt(UInt64.max) * BigUInt(UInt64.max) * BigUInt(UInt64.max)
check { BigUInt.randomInteger(lessThan: limit, using: &$0) }
}

}

0 comments on commit 97a80fb

Please sign in to comment.