diff --git a/Sources/Data Conversion.swift b/Sources/Data Conversion.swift index b74e683..613e74b 100644 --- a/Sources/Data Conversion.swift +++ b/Sources/Data Conversion.swift @@ -53,6 +53,7 @@ extension BigUInt { let byteCount = (self.bitWidth + 7) / 8 let buffer = UnsafeMutableBufferPointer.allocate(capacity: byteCount) + buffer.initialize(repeating: 0) guard byteCount > 0 else { return UnsafeRawBufferPointer(start: buffer.baseAddress, count: 0) } diff --git a/Sources/Floating Point Conversion.swift b/Sources/Floating Point Conversion.swift index 8c4b767..3120fd3 100644 --- a/Sources/Floating Point Conversion.swift +++ b/Sources/Floating Point Conversion.swift @@ -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 } diff --git a/Tests/BigIntTests/BigIntTests.swift b/Tests/BigIntTests/BigIntTests.swift index c46c022..6f71a20 100644 --- a/Tests/BigIntTests/BigIntTests.swift +++ b/Tests/BigIntTests/BigIntTests.swift @@ -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) @@ -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) diff --git a/Tests/BigIntTests/BigUIntTests.swift b/Tests/BigIntTests/BigUIntTests.swift index 08412ac..4b4a826 100644 --- a/Tests/BigIntTests/BigUIntTests.swift +++ b/Tests/BigIntTests/BigUIntTests.swift @@ -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) @@ -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) @@ -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) } } - }