Skip to content

Commit 892d3c5

Browse files
authored
Pull in swift-corelibs-foundation Decimal improvements (#741)
* Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: 81fddf2 Author: Butta <[email protected]> SR-13015: Decimal calculation incorrect on Linux resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: 965a32f Author: Simon Evans <[email protected]> SR-13837: Swift Decimal type crashes on a specific double value resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: 2d3c2ee Author: Simon Evans <[email protected]> Decimal: init?(string:) and scanDecimal() should return nil on error. resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: deb8292 Author: Xiaodi Wu <[email protected]> Synchronize Decimal overlay and corelibs-foundation implementations. resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: afbb0af Author: Xiaodi Wu <[email protected]> [SR-6785] Fix conformance of Decimal to Strideable. resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: 9fe6948 Author: Xiaodi Wu <[email protected]> [SR-15134] Fix exponent overflow/underflow in Decimal.init(sign:exponent:significand:). resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: 8a020be Author: Xiaodi Wu <[email protected]> Partially fix Decimal.ulp to return a valid representation. resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: 405aaec Author: Xiaodi Wu <[email protected]> Use 'Decimal.ulp' to compute 'nextUp' and 'nextDown'. resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: e8c418c Author: Xiaodi Wu <[email protected]> [SR-14974] Fix 'Decimal.ulp' to reflect spacing between consecutive values. resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: f0f0857 Author: Xiaodi Wu <[email protected]> [SR-15132] Fix Decimal implementation of significand and init(sign:exponent:significand:). resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: aed94d4 Author: Xiaodi Wu <[email protected]> Fix a subtle bug in Decimal.ulp and adjust tests. resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements swift-corelibs-foundation commit: 376c6b2 Author: Xiaodi Wu <[email protected]> Fix implementation of Decimal.nextUp. resolves: rdar://131892431 * Pull in swift-corelibs-foundation Decimal improvements - Remove the stale TestDecimal.swift resolves: rdar://131892431
1 parent 35d896a commit 892d3c5

File tree

3 files changed

+437
-75
lines changed

3 files changed

+437
-75
lines changed

Sources/FoundationEssentials/Decimal/Decimal+Conformances.swift

Lines changed: 162 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -170,16 +170,35 @@ extension Decimal /* : FloatingPoint */ {
170170
self = Decimal()
171171
let negative = value < 0
172172
var val = negative ? -1 * value : value
173-
var exponent = 0
173+
var exponent: Int8 = 0
174+
175+
// Try to get val as close to UInt64.max whilst adjusting the exponent
176+
// to reduce the number of digits after the decimal point.
174177
while val < Double(UInt64.max - 1) {
178+
guard exponent > Int8.min else {
179+
self = .nan
180+
return
181+
}
175182
val *= 10.0
176183
exponent -= 1
177184
}
178-
while Double(UInt64.max - 1) < val {
185+
while Double(UInt64.max) <= val {
186+
guard exponent < Int8.max else {
187+
self = .nan
188+
return
189+
}
179190
val /= 10.0
180191
exponent += 1
181192
}
182-
var mantissa = UInt64(val)
193+
var mantissa: UInt64
194+
let maxMantissa = Double(UInt64.max).nextDown
195+
if val > maxMantissa {
196+
// UInt64(Double(UInt64.max)) gives an overflow error,
197+
// this is the largest mantissa that can be set.
198+
mantissa = UInt64(maxMantissa)
199+
} else {
200+
mantissa = UInt64(val)
201+
}
183202

184203
var i: UInt32 = 0
185204
// This is a bit ugly but it is the closest approximation of the C
@@ -217,13 +236,24 @@ extension Decimal /* : FloatingPoint */ {
217236
}
218237

219238
public init(sign: FloatingPointSign, exponent: Int, significand: Decimal) {
220-
self.init(
221-
_exponent: Int32(exponent) + significand._exponent,
222-
_length: significand._length,
223-
_isNegative: sign == .plus ? 0 : 1,
224-
_isCompact: significand._isCompact,
225-
_reserved: 0,
226-
_mantissa: significand._mantissa)
239+
self = significand
240+
do {
241+
self = try significand._multiplyByPowerOfTen(
242+
power: exponent, roundingMode: .plain)
243+
} catch {
244+
guard let actual = error as? Decimal._CalculationError else {
245+
self = .nan
246+
return
247+
}
248+
if actual == .underflow {
249+
self = 0
250+
} else {
251+
self = .nan
252+
}
253+
}
254+
if sign == .minus {
255+
negate()
256+
}
227257
}
228258

229259
public init(signOf: Decimal, magnitudeOf magnitude: Decimal) {
@@ -242,7 +272,7 @@ extension Decimal /* : FloatingPoint */ {
242272

243273
public var significand: Decimal {
244274
return Decimal(
245-
_exponent: 0, _length: _length, _isNegative: _isNegative, _isCompact: _isCompact,
275+
_exponent: 0, _length: _length, _isNegative: 0, _isCompact: _isCompact,
246276
_reserved: 0, _mantissa: _mantissa)
247277
}
248278

@@ -251,9 +281,20 @@ extension Decimal /* : FloatingPoint */ {
251281
}
252282

253283
public var ulp: Decimal {
254-
if !self.isFinite { return Decimal.nan }
284+
guard isFinite else { return .nan }
285+
286+
let exponent: Int32
287+
if isZero {
288+
exponent = .min
289+
} else {
290+
let shift = _powersOfTenDividingUInt128Max.firstIndex {
291+
return significand > $0
292+
} ?? _powersOfTenDividingUInt128Max.count
293+
exponent = _exponent &- Int32(shift)
294+
}
295+
255296
return Decimal(
256-
_exponent: _exponent, _length: 8, _isNegative: 0, _isCompact: 1,
297+
_exponent: max(exponent, -128), _length: 1, _isNegative: 0, _isCompact: 1,
257298
_reserved: 0, _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000))
258299
}
259300

@@ -309,25 +350,38 @@ extension Decimal /* : FloatingPoint */ {
309350
public mutating func formTruncatingRemainder(dividingBy other: Decimal) { fatalError("Decimal does not yet fully adopt FloatingPoint") }
310351

311352
public var nextUp: Decimal {
312-
return self + Decimal(
313-
_exponent: _exponent,
314-
_length: 1,
315-
_isNegative: 0,
316-
_isCompact: 1,
317-
_reserved: 0,
318-
_mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)
319-
)
353+
if _isNegative == 1 {
354+
if _exponent > -128 &&
355+
(_mantissa.0, _mantissa.1, _mantissa.2, _mantissa.3) == (0x999a, 0x9999, 0x9999, 0x9999) &&
356+
(_mantissa.4, _mantissa.5, _mantissa.6, _mantissa.7) == (0x9999, 0x9999, 0x9999, 0x1999) {
357+
return Decimal(
358+
_exponent: _exponent &- 1,
359+
_length: 8,
360+
_isNegative: 1,
361+
_isCompact: 1,
362+
_reserved: 0,
363+
_mantissa: (0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)
364+
)
365+
}
366+
} else {
367+
if _exponent < 127 &&
368+
(_mantissa.0, _mantissa.1, _mantissa.2, _mantissa.3) == (0xffff, 0xffff, 0xffff, 0xffff) &&
369+
(_mantissa.4, _mantissa.5, _mantissa.6, _mantissa.7) == (0xffff, 0xffff, 0xffff, 0xffff) {
370+
return Decimal(
371+
_exponent: _exponent &+ 1,
372+
_length: 8,
373+
_isNegative: 0,
374+
_isCompact: 1,
375+
_reserved: 0,
376+
_mantissa: (0x999a, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x1999)
377+
)
378+
}
379+
}
380+
return self + ulp
320381
}
321382

322383
public var nextDown: Decimal {
323-
return self - Decimal(
324-
_exponent: _exponent,
325-
_length: 1,
326-
_isNegative: 0,
327-
_isCompact: 1,
328-
_reserved: 0,
329-
_mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)
330-
)
384+
return -(-self).nextUp
331385
}
332386

333387
public func isEqual(to other: Decimal) -> Bool {
@@ -604,30 +658,38 @@ extension Decimal : SignedNumeric {
604658
#endif
605659

606660
public static func +=(lhs: inout Decimal, rhs: Decimal) {
607-
let result = try? lhs._add(rhs: rhs, roundingMode: .plain)
608-
if let result = result {
661+
do {
662+
let result = try lhs._add(rhs: rhs, roundingMode: .plain)
609663
lhs = result.result
664+
} catch {
665+
lhs = .nan
610666
}
611667
}
612668

613669
public static func -=(lhs: inout Decimal, rhs: Decimal) {
614-
let result = try? lhs._subtract(rhs: rhs, roundingMode: .plain)
615-
if let result = result {
670+
do {
671+
let result = try lhs._subtract(rhs: rhs, roundingMode: .plain)
616672
lhs = result
673+
} catch {
674+
lhs = .nan
617675
}
618676
}
619677

620678
public static func *=(lhs: inout Decimal, rhs: Decimal) {
621-
let result = try? lhs._multiply(by: rhs, roundingMode: .plain)
622-
if let result = result {
679+
do {
680+
let result = try lhs._multiply(by: rhs, roundingMode: .plain)
623681
lhs = result
682+
} catch {
683+
lhs = .nan
624684
}
625685
}
626686

627687
public static func /=(lhs: inout Decimal, rhs: Decimal) {
628-
let result = try? lhs._divide(by: rhs, roundingMode: .plain)
629-
if let result = result {
688+
do {
689+
let result = try lhs._divide(by: rhs, roundingMode: .plain)
630690
lhs = result
691+
} catch {
692+
lhs = .nan
631693
}
632694
}
633695

@@ -664,10 +726,72 @@ extension Decimal : SignedNumeric {
664726
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
665727
extension Decimal : Strideable {
666728
public func distance(to other: Decimal) -> Decimal {
667-
return self - other
729+
return other - self
668730
}
669731

670732
public func advanced(by n: Decimal) -> Decimal {
671733
return self + n
672734
}
673735
}
736+
737+
// Max power
738+
private extension Decimal {
739+
// Creates a value with zero exponent.
740+
// (Used by `_powersOfTenDividingUInt128Max`.)
741+
init(
742+
_length: UInt32,
743+
_isCompact: UInt32,
744+
_mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)
745+
) {
746+
self.init(
747+
_exponent: 0,
748+
_length: _length,
749+
_isNegative: 0,
750+
_isCompact: _isCompact,
751+
_reserved: 0,
752+
_mantissa: _mantissa
753+
)
754+
}
755+
}
756+
757+
private let _powersOfTenDividingUInt128Max = [
758+
/* 10**00 dividing UInt128.max is deliberately omitted. */
759+
/* 10**01 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x1999)),
760+
/* 10**02 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0xf5c2, 0x5c28, 0xc28f, 0x28f5, 0x8f5c, 0xf5c2, 0x5c28, 0x028f)),
761+
/* 10**03 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0x1893, 0x5604, 0x2d0e, 0x9db2, 0xa7ef, 0x4bc6, 0x8937, 0x0041)),
762+
/* 10**04 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0x0275, 0x089a, 0x9e1b, 0x295e, 0x10cb, 0xbac7, 0x8db8, 0x0006)),
763+
/* 10**05 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x3372, 0x80dc, 0x0fcf, 0x8423, 0x1b47, 0xac47, 0xa7c5,0)),
764+
/* 10**06 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x3858, 0xf349, 0xb4c7, 0x8d36, 0xb5ed, 0xf7a0, 0x10c6,0)),
765+
/* 10**07 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0xec08, 0x6520, 0x787a, 0xf485, 0xabca, 0x7f29, 0x01ad,0)),
766+
/* 10**08 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x4acd, 0x7083, 0xbf3f, 0x1873, 0xc461, 0xf31d, 0x002a,0)),
767+
/* 10**09 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x5447, 0x8b40, 0x2cb9, 0xb5a5, 0xfa09, 0x4b82, 0x0004,0)),
768+
/* 10**10 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0xa207, 0x5ab9, 0xeadf, 0x5ef6, 0x7f67, 0x6df3,0,0)),
769+
/* 10**11 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0xf69a, 0xef78, 0x4aaf, 0xbcb2, 0xbff0, 0x0afe,0,0)),
770+
/* 10**12 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0x7f0f, 0x97f2, 0xa111, 0x12de, 0x7998, 0x0119,0,0)),
771+
/* 10**13 */ Decimal(_length: 6, _isCompact: 0, _mantissa: (0x0cb4, 0xc265, 0x7681, 0x6849, 0x25c2, 0x001c,0,0)),
772+
/* 10**14 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0x4e12, 0x603d, 0x2573, 0x70d4, 0xd093, 0x0002,0,0)),
773+
/* 10**15 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0x87ce, 0x566c, 0x9d58, 0xbe7b, 0x480e,0,0,0)),
774+
/* 10**16 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0xda61, 0x6f0a, 0xf622, 0xaca5, 0x0734,0,0,0)),
775+
/* 10**17 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0x4909, 0xa4b4, 0x3236, 0x77aa, 0x00b8,0,0,0)),
776+
/* 10**18 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0xa0e7, 0x43ab, 0xd1d2, 0x725d, 0x0012,0,0,0)),
777+
/* 10**19 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0xc34a, 0x6d2a, 0x94fb, 0xd83c, 0x0001,0,0,0)),
778+
/* 10**20 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x46ba, 0x2484, 0x4219, 0x2f39,0,0,0,0)),
779+
/* 10**21 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0xd3df, 0x83a6, 0xed02, 0x04b8,0,0,0,0)),
780+
/* 10**22 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x7b96, 0x405d, 0xe480, 0x0078,0,0,0,0)),
781+
/* 10**23 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x5928, 0xa009, 0x16d9, 0x000c,0,0,0,0)),
782+
/* 10**24 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x88ea, 0x299a, 0x357c, 0x0001,0,0,0,0)),
783+
/* 10**25 */ Decimal(_length: 3, _isCompact: 1, _mantissa: (0xda7d, 0xd0f5, 0x1ef2,0,0,0,0,0)),
784+
/* 10**26 */ Decimal(_length: 3, _isCompact: 1, _mantissa: (0x95d9, 0x4818, 0x0318,0,0,0,0,0)),
785+
/* 10**27 */ Decimal(_length: 3, _isCompact: 0, _mantissa: (0xdbc8, 0x3a68, 0x004f,0,0,0,0,0)),
786+
/* 10**28 */ Decimal(_length: 3, _isCompact: 1, _mantissa: (0xaf94, 0xec3d, 0x0007,0,0,0,0,0)),
787+
/* 10**29 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0xf7f5, 0xcad2,0,0,0,0,0,0)),
788+
/* 10**30 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0x4bfe, 0x1448,0,0,0,0,0,0)),
789+
/* 10**31 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0x3acc, 0x0207,0,0,0,0,0,0)),
790+
/* 10**32 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0xec47, 0x0033,0,0,0,0,0,0)),
791+
/* 10**33 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0x313a, 0x0005,0,0,0,0,0,0)),
792+
/* 10**34 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x84ec,0,0,0,0,0,0,0)),
793+
/* 10**35 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x0d4a,0,0,0,0,0,0,0)),
794+
/* 10**36 */ Decimal(_length: 1, _isCompact: 0, _mantissa: (0x0154,0,0,0,0,0,0,0)),
795+
/* 10**37 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x0022,0,0,0,0,0,0,0)),
796+
/* 10**38 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x0003,0,0,0,0,0,0,0))
797+
]

Sources/FoundationEssentials/Decimal/Decimal+Math.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,11 @@ extension Decimal {
650650
}
651651
}
652652

653-
private mutating func copyVariableLengthInteger(_ source: VariableLengthInteger) throws {
653+
internal mutating func copyVariableLengthInteger(_ source: VariableLengthInteger) throws {
654+
guard source.count <= Decimal.maxSize else {
655+
throw _CalculationError.overflow
656+
}
657+
self._length = UInt32(source.count)
654658
switch source.count {
655659
case 0:
656660
self._mantissa = (0, 0, 0, 0, 0, 0, 0, 0)
@@ -948,10 +952,8 @@ extension Decimal {
948952
carry = acc >> 16
949953
// FIXME: Check if truncate is okay here
950954
result[j + i] = UInt16(truncatingIfNeeded:acc) & 0xFFFF
951-
} else {
952-
if !(carry == 0 && (rhs[j] == 0 || lhs[i] == 0)) {
953-
throw _CalculationError.overflow
954-
}
955+
} else if carry != 0 || (rhs[j] > 0 && lhs[i] > 0) {
956+
throw _CalculationError.overflow
955957
}
956958
}
957959

0 commit comments

Comments
 (0)