Skip to content

Commit 151ec1d

Browse files
authored
Merge pull request #1550 from spevans/pr_sr_7650
2 parents b1647ac + 5dd39d3 commit 151ec1d

File tree

2 files changed

+130
-74
lines changed

2 files changed

+130
-74
lines changed

Foundation/Decimal.swift

Lines changed: 98 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -441,12 +441,39 @@ extension Decimal {
441441
self.compact()
442442
}
443443
}
444+
444445
public init(_ value: UInt64) {
445-
self.init(Double(value))
446+
self = Decimal()
447+
if value == 0 {
448+
return
449+
}
450+
451+
var compactValue = value
452+
var exponent: Int32 = 0
453+
while compactValue % 10 == 0 {
454+
compactValue = compactValue / 10
455+
exponent = exponent + 1
456+
}
457+
_isCompact = 1
458+
_exponent = exponent
459+
460+
let wordCount = ((UInt64.bitWidth - compactValue.leadingZeroBitCount) + (UInt16.bitWidth - 1)) / UInt16.bitWidth
461+
_length = UInt32(wordCount)
462+
_mantissa.0 = UInt16(truncatingIfNeeded: compactValue >> 0)
463+
_mantissa.1 = UInt16(truncatingIfNeeded: compactValue >> 16)
464+
_mantissa.2 = UInt16(truncatingIfNeeded: compactValue >> 32)
465+
_mantissa.3 = UInt16(truncatingIfNeeded: compactValue >> 48)
446466
}
467+
447468
public init(_ value: Int64) {
448-
self.init(Double(value))
469+
if value < 0 {
470+
self.init(value == Int64.min ? UInt64(Int64.max) + 1 : UInt64(abs(value)))
471+
_isNegative = 1
472+
} else {
473+
self.init(UInt64(value))
474+
}
449475
}
476+
450477
public init(_ value: UInt) {
451478
self.init(UInt64(value))
452479
}
@@ -1164,62 +1191,60 @@ public func NSDecimalAdd(_ result: UnsafeMutablePointer<Decimal>, _ leftOperand:
11641191
}
11651192

11661193
fileprivate func integerAdd(_ result: inout WideDecimal, _ left: inout Decimal, _ right: inout Decimal) -> NSDecimalNumber.CalculationError {
1167-
var i:UInt32 = 0
1168-
var carry:UInt16 = 0
1169-
var accumulator:UInt32 = 0
1170-
1171-
let c:UInt32 = min(left._length, right._length)
1194+
var idx: UInt32 = 0
1195+
var carry: UInt16 = 0
1196+
let maxIndex: UInt32 = min(left._length, right._length) // The highest index with bits set in both values
11721197

1173-
while i < c {
1174-
let li = UInt32(left[i])
1175-
let ri = UInt32(right[i])
1176-
accumulator = li + ri + UInt32(carry)
1177-
carry = UInt16(truncatingIfNeeded:accumulator >> 16)
1178-
result[i] = UInt16(truncatingIfNeeded:accumulator)
1179-
i += 1
1198+
while idx < maxIndex {
1199+
let li = UInt32(left[idx])
1200+
let ri = UInt32(right[idx])
1201+
let sum = li + ri + UInt32(carry)
1202+
carry = UInt16(truncatingIfNeeded: sum >> 16)
1203+
result[idx] = UInt16(truncatingIfNeeded: sum)
1204+
idx += 1
11801205
}
11811206

1182-
while i < left._length {
1207+
while idx < left._length {
11831208
if carry != 0 {
1184-
let li = UInt32(left[i])
1185-
accumulator = li + UInt32(carry)
1186-
carry = UInt16(truncatingIfNeeded:accumulator >> 16)
1187-
result[i] = UInt16(truncatingIfNeeded:accumulator)
1188-
i += 1
1209+
let li = UInt32(left[idx])
1210+
let sum = li + UInt32(carry)
1211+
carry = UInt16(truncatingIfNeeded: sum >> 16)
1212+
result[idx] = UInt16(truncatingIfNeeded: sum)
1213+
idx += 1
11891214
} else {
1190-
while i < left._length {
1191-
result[i] = left[i]
1192-
i += 1
1215+
while idx < left._length {
1216+
result[idx] = left[idx]
1217+
idx += 1
11931218
}
11941219
break
11951220
}
11961221
}
1197-
while i < right._length {
1222+
while idx < right._length {
11981223
if carry != 0 {
1199-
let ri = UInt32(right[i])
1200-
accumulator = ri + UInt32(carry)
1201-
carry = UInt16(truncatingIfNeeded:accumulator >> 16)
1202-
result[i] = UInt16(truncatingIfNeeded:accumulator)
1203-
i += 1
1224+
let ri = UInt32(right[idx])
1225+
let sum = ri + UInt32(carry)
1226+
carry = UInt16(truncatingIfNeeded: sum >> 16)
1227+
result[idx] = UInt16(truncatingIfNeeded: sum)
1228+
idx += 1
12041229
} else {
1205-
while i < right._length {
1206-
result[i] = right[i]
1207-
i += 1
1230+
while idx < right._length {
1231+
result[idx] = right[idx]
1232+
idx += 1
12081233
}
12091234
break
12101235
}
12111236
}
1237+
result._length = idx
12121238

12131239
if carry != 0 {
1214-
if result._length < i {
1215-
result._length = i
1216-
return .overflow
1217-
} else {
1218-
result[i] = carry
1219-
i += 1
1220-
}
1240+
result[idx] = carry
1241+
idx += 1
1242+
result._length = idx
12211243
}
1222-
result._length = i;
1244+
if idx > Decimal.maxSize {
1245+
return .overflow
1246+
}
1247+
12231248
return .noError;
12241249
}
12251250

@@ -1231,49 +1256,48 @@ fileprivate func integerAdd(_ result: inout WideDecimal, _ left: inout Decimal,
12311256
// give b-a...
12321257
//
12331258
fileprivate func integerSubtract(_ result: inout Decimal, _ left: inout Decimal, _ right: inout Decimal) -> NSDecimalNumber.CalculationError {
1234-
var i:UInt32 = 0
1235-
var carry:UInt16 = 1
1236-
var accumulator:UInt32 = 0
1237-
1238-
let c:UInt32 = min(left._length, right._length)
1239-
1240-
while i < c {
1241-
let li = UInt32(left[i])
1242-
let ri = UInt32(right[i])
1243-
accumulator = 0xffff + li - ri + UInt32(carry)
1244-
carry = UInt16(truncatingIfNeeded:accumulator >> 16)
1245-
result[i] = UInt16(truncatingIfNeeded:accumulator)
1246-
i += 1
1247-
}
1248-
1249-
while i < left._length {
1250-
if carry != 0 {
1251-
let li = UInt32(left[i])
1252-
accumulator = 0xffff + li // + no carry
1253-
carry = UInt16(truncatingIfNeeded:accumulator >> 16)
1254-
result[i] = UInt16(truncatingIfNeeded:accumulator)
1255-
i += 1
1259+
var idx: UInt32 = 0
1260+
let maxIndex: UInt32 = min(left._length, right._length) // The highest index with bits set in both values
1261+
var borrow: UInt16 = 0
1262+
1263+
while idx < maxIndex {
1264+
let li = UInt32(left[idx])
1265+
let ri = UInt32(right[idx])
1266+
// 0x10000 is to borrow in advance to avoid underflow.
1267+
let difference: UInt32 = (0x10000 + li) - UInt32(borrow) - ri
1268+
result[idx] = UInt16(truncatingIfNeeded: difference)
1269+
// borrow = 1 if the borrow was used.
1270+
borrow = 1 - UInt16(truncatingIfNeeded: difference >> 16)
1271+
idx += 1
1272+
}
1273+
1274+
while idx < left._length {
1275+
if borrow != 0 {
1276+
let li = UInt32(left[idx])
1277+
let sum = 0xffff + li // + no carry
1278+
borrow = 1 - UInt16(truncatingIfNeeded: sum >> 16)
1279+
result[idx] = UInt16(truncatingIfNeeded: sum)
1280+
idx += 1
12561281
} else {
1257-
while i < left._length {
1258-
result[i] = left[i]
1259-
i += 1
1282+
while idx < left._length {
1283+
result[idx] = left[idx]
1284+
idx += 1
12601285
}
12611286
break
12621287
}
12631288
}
1264-
while i < right._length {
1265-
let ri = UInt32(right[i])
1266-
accumulator = 0xffff - ri + UInt32(carry)
1267-
carry = UInt16(truncatingIfNeeded:accumulator >> 16)
1268-
result[i] = UInt16(truncatingIfNeeded:accumulator)
1269-
i += 1
1289+
while idx < right._length {
1290+
let ri = UInt32(right[idx])
1291+
let difference = 0xffff - ri + UInt32(borrow)
1292+
borrow = 1 - UInt16(truncatingIfNeeded: difference >> 16)
1293+
result[idx] = UInt16(truncatingIfNeeded: difference)
1294+
idx += 1
12701295
}
12711296

1272-
if carry != 0 {
1297+
if borrow != 0 {
12731298
return .overflow
12741299
}
1275-
result._length = i;
1276-
1300+
result._length = idx;
12771301
result.trimTrailingZeros()
12781302

12791303
return .noError;

TestFoundation/TestDecimal.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ class TestDecimal: XCTestCase {
124124
XCTAssertFalse(zero.isInfinite)
125125
XCTAssertFalse(zero.isNaN)
126126
XCTAssertFalse(zero.isSignaling)
127+
128+
let d1 = Decimal(1234567890123456789 as UInt64)
129+
XCTAssertEqual(d1._exponent, 0)
130+
XCTAssertEqual(d1._length, 4)
127131
}
128132
func test_Constants() {
129133
XCTAssertEqual(8, NSDecimalMaxSize)
@@ -273,7 +277,31 @@ class TestDecimal: XCTestCase {
273277
}
274278
}
275279
}
280+
276281
XCTAssertEqual(Decimal(186243 * 15673 as Int64), Decimal(186243) * Decimal(15673))
282+
283+
XCTAssertEqual(Decimal(string: "5538")! + Decimal(string: "2880.4")!, Decimal(string: "8418.4")!)
284+
XCTAssertEqual(NSDecimalNumber(floatLiteral: 5538).adding(NSDecimalNumber(floatLiteral: 2880.4)), NSDecimalNumber(floatLiteral: 5538 + 2880.4))
285+
286+
XCTAssertEqual(Decimal(string: "5538.0")! - Decimal(string: "2880.4")!, Decimal(string: "2657.6")!)
287+
XCTAssertEqual(Decimal(string: "2880.4")! - Decimal(5538), Decimal(string: "-2657.6")!)
288+
XCTAssertEqual(Decimal(0x10000) - Decimal(0x1000), Decimal(0xf000))
289+
XCTAssertEqual(Decimal(0x1_0000_0000) - Decimal(0x1000), Decimal(0xFFFFF000))
290+
XCTAssertEqual(Decimal(0x1_0000_0000_0000) - Decimal(0x1000), Decimal(0xFFFFFFFFF000))
291+
XCTAssertEqual(Decimal(1234_5678_9012_3456_7899 as UInt64) - Decimal(1234_5678_9012_3456_7890 as UInt64), Decimal(9))
292+
XCTAssertEqual(Decimal(0xffdd_bb00_8866_4422 as UInt64) - Decimal(0x7777_7777), Decimal(0xFFDD_BB00_10EE_CCAB as UInt64))
293+
XCTAssertEqual(NSDecimalNumber(floatLiteral: 5538).subtracting(NSDecimalNumber(floatLiteral: 2880.4)), NSDecimalNumber(floatLiteral: 5538 - 2880.4))
294+
XCTAssertEqual(NSDecimalNumber(floatLiteral: 2880.4).subtracting(NSDecimalNumber(floatLiteral: 5538)), NSDecimalNumber(floatLiteral: 2880.4 - 5538))
295+
296+
XCTAssertEqual(Decimal.greatestFiniteMagnitude - Decimal.greatestFiniteMagnitude, Decimal(0))
297+
XCTAssertEqual(Decimal.leastFiniteMagnitude - Decimal(1), Decimal.leastFiniteMagnitude)
298+
let overflowed = Decimal.greatestFiniteMagnitude + Decimal.greatestFiniteMagnitude
299+
XCTAssertTrue(overflowed.isNaN)
300+
301+
let highBit = Decimal(_exponent: 0, _length: 8, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8000))
302+
let otherBits = Decimal(_exponent: 0, _length: 8, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x7fff))
303+
XCTAssertEqual(highBit - otherBits, Decimal(1))
304+
XCTAssertEqual(otherBits + Decimal(1), highBit)
277305
}
278306

279307
func test_Misc() {
@@ -478,6 +506,10 @@ class TestDecimal: XCTestCase {
478506
XCTAssertEqual(1, f._isCompact)
479507
let after = f.description
480508
XCTAssertEqual(before, after)
509+
510+
let nsd1 = NSDecimalNumber(decimal: Decimal(2657.6))
511+
let nsd2 = NSDecimalNumber(floatLiteral: 2657.6)
512+
XCTAssertEqual(nsd1, nsd2)
481513
}
482514

483515
func test_PositivePowers() {

0 commit comments

Comments
 (0)