Skip to content

Commit af2b6c6

Browse files
committed
Implement Decimal.init?<T : BinaryInteger>(exactly source: T)
- This still requires some tests with a BinaryInteger larger then UInt64.
1 parent 653c070 commit af2b6c6

File tree

2 files changed

+88
-4
lines changed

2 files changed

+88
-4
lines changed

Foundation/Decimal.swift

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
88
//
99

10-
import CoreFoundation
11-
1210
public var NSDecimalMaxSize: Int32 { 8 }
1311
public var NSDecimalNoScale: Int32 { Int32(Int16.max) }
1412

@@ -349,9 +347,50 @@ extension Decimal : SignedNumeric {
349347
_reserved: 0, _mantissa: self._mantissa)
350348
}
351349

352-
// FIXME(integers): implement properly
353350
public init?<T : BinaryInteger>(exactly source: T) {
354-
fatalError()
351+
let zero = 0 as T
352+
353+
if source == zero {
354+
self = Decimal.zero
355+
return
356+
}
357+
358+
let negative: UInt32 = (T.isSigned && source < zero) ? 1 : 0
359+
var mantissa = source.magnitude
360+
var exponent: Int32 = 0
361+
362+
let maxExponent = type(of: __exponent).max
363+
while mantissa.isMultiple(of: 10) && (exponent < maxExponent) {
364+
exponent += 1
365+
mantissa /= 10
366+
}
367+
368+
// If the matinssa still requires more than 128bits of storage then it is too large.
369+
if mantissa.bitWidth > 128 && (mantissa >> 128 != zero) { return nil }
370+
371+
let mantissaParts: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)
372+
let loWord = UInt64(truncatingIfNeeded: mantissa)
373+
var length = ((loWord.bitWidth - loWord.leadingZeroBitCount) + (UInt16.bitWidth - 1)) / UInt16.bitWidth
374+
mantissaParts.0 = UInt16(truncatingIfNeeded: loWord >> 0)
375+
mantissaParts.1 = UInt16(truncatingIfNeeded: loWord >> 16)
376+
mantissaParts.2 = UInt16(truncatingIfNeeded: loWord >> 32)
377+
mantissaParts.3 = UInt16(truncatingIfNeeded: loWord >> 48)
378+
379+
let hiWord = mantissa.bitWidth > 64 ? UInt64(truncatingIfNeeded: mantissa >> 64) : 0
380+
if hiWord != 0 {
381+
length = 4 + ((hiWord.bitWidth - hiWord.leadingZeroBitCount) + (UInt16.bitWidth - 1)) / UInt16.bitWidth
382+
mantissaParts.4 = UInt16(truncatingIfNeeded: hiWord >> 0)
383+
mantissaParts.5 = UInt16(truncatingIfNeeded: hiWord >> 16)
384+
mantissaParts.6 = UInt16(truncatingIfNeeded: hiWord >> 32)
385+
mantissaParts.7 = UInt16(truncatingIfNeeded: hiWord >> 48)
386+
} else {
387+
mantissaParts.4 = 0
388+
mantissaParts.5 = 0
389+
mantissaParts.6 = 0
390+
mantissaParts.7 = 0
391+
}
392+
393+
self = Decimal(_exponent: exponent, _length: UInt32(length), _isNegative: negative, _isCompact: 1, _reserved: 0, _mantissa: mantissaParts)
355394
}
356395

357396
public static func +=(lhs: inout Decimal, rhs: Decimal) {

TestFoundation/TestDecimal.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,50 @@ class TestDecimal: XCTestCase {
11631163
XCTAssertEqual(NSDecimalNumber(value: 1).multiplying(byPowerOf10: -129).stringValue, "NaN")
11641164
}
11651165

1166+
func test_initExactly() {
1167+
// This really requires some tests using a BinaryInteger of bitwidth > 128 to test failures.
1168+
let d1 = Decimal(exactly: UInt64.max)
1169+
XCTAssertNotNil(d1)
1170+
XCTAssertEqual(d1?.description, UInt64.max.description)
1171+
XCTAssertEqual(d1?._length, 4)
1172+
1173+
let d2 = Decimal(exactly: Int64.min)
1174+
XCTAssertNotNil(d2)
1175+
XCTAssertEqual(d2?.description, Int64.min.description)
1176+
XCTAssertEqual(d2?._length, 4)
1177+
1178+
let d3 = Decimal(exactly: Int64.max)
1179+
XCTAssertNotNil(d3)
1180+
XCTAssertEqual(d3?.description, Int64.max.description)
1181+
XCTAssertEqual(d3?._length, 4)
1182+
1183+
let d4 = Decimal(exactly: Int32.min)
1184+
XCTAssertNotNil(d4)
1185+
XCTAssertEqual(d4?.description, Int32.min.description)
1186+
XCTAssertEqual(d4?._length, 2)
1187+
1188+
let d5 = Decimal(exactly: Int32.max)
1189+
XCTAssertNotNil(d5)
1190+
XCTAssertEqual(d5?.description, Int32.max.description)
1191+
XCTAssertEqual(d5?._length, 2)
1192+
1193+
let d6 = Decimal(exactly: 0)
1194+
XCTAssertNotNil(d6)
1195+
XCTAssertEqual(d6, Decimal.zero)
1196+
XCTAssertEqual(d6?.description, "0")
1197+
XCTAssertEqual(d6?._length, 0)
1198+
1199+
let d7 = Decimal(exactly: 1)
1200+
XCTAssertNotNil(d7)
1201+
XCTAssertEqual(d7?.description, "1")
1202+
XCTAssertEqual(d7?._length, 1)
1203+
1204+
let d8 = Decimal(exactly: -1)
1205+
XCTAssertNotNil(d8)
1206+
XCTAssertEqual(d8?.description, "-1")
1207+
XCTAssertEqual(d8?._length, 1)
1208+
}
1209+
11661210
static var allTests : [(String, (TestDecimal) -> () throws -> Void)] {
11671211
return [
11681212
("test_NSDecimalNumberInit", test_NSDecimalNumberInit),
@@ -1191,6 +1235,7 @@ class TestDecimal: XCTestCase {
11911235
("test_stringWithLocale", test_stringWithLocale),
11921236
("test_NSDecimalString", test_NSDecimalString),
11931237
("test_multiplyingByPowerOf10", test_multiplyingByPowerOf10),
1238+
("test_initExactly", test_initExactly),
11941239
]
11951240
}
11961241
}

0 commit comments

Comments
 (0)