Skip to content

Commit d0d8a18

Browse files
authored
Introduce Decimal arithmetics (#735)
This patch implements Decimal arithmithcs (plus, minus, power, etc) which completes Decimal's implementation. resolves rdar://130556160
1 parent 7186496 commit d0d8a18

File tree

6 files changed

+1891
-154
lines changed

6 files changed

+1891
-154
lines changed

Sources/FoundationEssentials/Decimal/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
##===----------------------------------------------------------------------===##
1414
target_sources(FoundationEssentials PRIVATE
1515
Decimal.swift
16-
Decimal+Conformances.swift)
16+
Decimal+Conformances.swift
17+
Decimal+Math.swift)

Sources/FoundationEssentials/Decimal/Decimal+Conformances.swift

Lines changed: 167 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extension Decimal : CustomStringConvertible {
2222
if let decimalSeparator = locale?.decimalSeparator {
2323
decimalString = decimalString.replacing(decimalSeparator, with: ".")
2424
}
25-
guard let value = Decimal.decimal(from: decimalString.utf8, matchEntireString: false) else {
25+
guard let value = Decimal.decimal(from: decimalString.utf8, matchEntireString: false).result else {
2626
return nil
2727
}
2828
self = value
@@ -35,11 +35,8 @@ extension Decimal : CustomStringConvertible {
3535

3636
// The methods in this extension exist to match the protocol requirements of
3737
// FloatingPoint, even if we can't conform directly.
38-
//
39-
// If it becomes clear that conformance is truly impossible, we can deprecate
40-
// some of the methods (e.g. `isEqual(to:)` in favor of operators).
4138
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
42-
extension Decimal {
39+
extension Decimal /* : FloatingPoint */ {
4340
public static let leastFiniteMagnitude = Decimal(
4441
_exponent: 127,
4542
_length: 8,
@@ -310,6 +307,56 @@ extension Decimal {
310307

311308
@available(*, unavailable, message: "Decimal does not yet fully adopt FloatingPoint.")
312309
public mutating func formTruncatingRemainder(dividingBy other: Decimal) { fatalError("Decimal does not yet fully adopt FloatingPoint") }
310+
311+
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+
)
320+
}
321+
322+
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+
)
331+
}
332+
333+
public func isEqual(to other: Decimal) -> Bool {
334+
return self == other
335+
}
336+
337+
public func isLess(than other: Decimal) -> Bool {
338+
return Decimal._compare(lhs: self, rhs: other) == .orderedAscending
339+
}
340+
341+
public func isLessThanOrEqualTo(_ other: Decimal) -> Bool {
342+
let order = Decimal._compare(lhs: self, rhs: other)
343+
return order == .orderedAscending || order == .orderedSame
344+
}
345+
346+
public func isTotallyOrdered(belowOrEqualTo other: Decimal) -> Bool {
347+
// Note: Decimal does not have -0 or infinities to worry about
348+
if self.isNaN {
349+
return false
350+
}
351+
if self < other {
352+
return true
353+
}
354+
if other < self {
355+
return false
356+
}
357+
// Fall through to == behavior
358+
return true
359+
}
313360
}
314361

315362
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
@@ -388,6 +435,52 @@ extension Decimal: Hashable {
388435
}
389436
}
390437

438+
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
439+
extension Decimal : Equatable {
440+
public static func ==(lhs: Decimal, rhs: Decimal) -> Bool {
441+
#if FOUNDATION_FRAMEWORK
442+
let bitwiseEqual: Bool =
443+
lhs._exponent == rhs._exponent &&
444+
lhs._length == rhs._length &&
445+
lhs._isNegative == rhs._isNegative &&
446+
lhs._isCompact == rhs._isCompact &&
447+
lhs._reserved == rhs._reserved &&
448+
lhs._mantissa.0 == rhs._mantissa.0 &&
449+
lhs._mantissa.1 == rhs._mantissa.1 &&
450+
lhs._mantissa.2 == rhs._mantissa.2 &&
451+
lhs._mantissa.3 == rhs._mantissa.3 &&
452+
lhs._mantissa.4 == rhs._mantissa.4 &&
453+
lhs._mantissa.5 == rhs._mantissa.5 &&
454+
lhs._mantissa.6 == rhs._mantissa.6 &&
455+
lhs._mantissa.7 == rhs._mantissa.7
456+
#else
457+
let bitwiseEqual: Bool =
458+
lhs.storage.exponent == rhs.storage.exponent &&
459+
lhs.storage.lengthFlagsAndReserved == rhs.storage.lengthFlagsAndReserved &&
460+
lhs.storage.reserved == rhs.storage.reserved &&
461+
lhs.storage.mantissa.0 == rhs.storage.mantissa.0 &&
462+
lhs.storage.mantissa.1 == rhs.storage.mantissa.1 &&
463+
lhs.storage.mantissa.2 == rhs.storage.mantissa.2 &&
464+
lhs.storage.mantissa.3 == rhs.storage.mantissa.3 &&
465+
lhs.storage.mantissa.4 == rhs.storage.mantissa.4 &&
466+
lhs.storage.mantissa.5 == rhs.storage.mantissa.5 &&
467+
lhs.storage.mantissa.6 == rhs.storage.mantissa.6 &&
468+
lhs.storage.mantissa.7 == rhs.storage.mantissa.7
469+
#endif
470+
if bitwiseEqual {
471+
return true
472+
}
473+
return Decimal._compare(lhs: lhs, rhs: rhs) == .orderedSame
474+
}
475+
}
476+
477+
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
478+
extension Decimal : Comparable {
479+
public static func <(lhs: Decimal, rhs: Decimal) -> Bool {
480+
return Decimal._compare(lhs: lhs, rhs: rhs) == .orderedAscending
481+
}
482+
}
483+
391484
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
392485
extension Decimal : Codable {
393486
private enum CodingKeys : Int, CodingKey {
@@ -445,10 +538,8 @@ extension Decimal : Codable {
445538
}
446539

447540
// MARK: - SignedNumeric
448-
// SwiftFoundation's `Decimal` does not fully conform to
449-
// SignedNumeric yet because no arithmetics have been implemented
450541
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
451-
extension Decimal /* : SignedNumeric */ {
542+
extension Decimal : SignedNumeric {
452543
public var magnitude: Decimal {
453544
guard _length != 0 else { return self }
454545
return Decimal(
@@ -510,10 +601,73 @@ extension Decimal /* : SignedNumeric */ {
510601
return Decimal(0)
511602
}
512603
}
513-
#else
514-
// We need this symbol until Decimal fully conform to SignedNumeric
515-
public static var zero: Decimal {
516-
return Decimal(0)
517-
}
518604
#endif
605+
606+
public static func +=(lhs: inout Decimal, rhs: Decimal) {
607+
let result = try? lhs._add(rhs: rhs, roundingMode: .plain)
608+
if let result = result {
609+
lhs = result.result
610+
}
611+
}
612+
613+
public static func -=(lhs: inout Decimal, rhs: Decimal) {
614+
let result = try? lhs._subtract(rhs: rhs, roundingMode: .plain)
615+
if let result = result {
616+
lhs = result
617+
}
618+
}
619+
620+
public static func *=(lhs: inout Decimal, rhs: Decimal) {
621+
let result = try? lhs._multiply(by: rhs, roundingMode: .plain)
622+
if let result = result {
623+
lhs = result
624+
}
625+
}
626+
627+
public static func /=(lhs: inout Decimal, rhs: Decimal) {
628+
let result = try? lhs._divide(by: rhs, roundingMode: .plain)
629+
if let result = result {
630+
lhs = result
631+
}
632+
}
633+
634+
public static func +(lhs: Decimal, rhs: Decimal) -> Decimal {
635+
var answer = lhs
636+
answer += rhs
637+
return answer
638+
}
639+
640+
public static func -(lhs: Decimal, rhs: Decimal) -> Decimal {
641+
var answer = lhs
642+
answer -= rhs
643+
return answer
644+
}
645+
646+
public static func *(lhs: Decimal, rhs: Decimal) -> Decimal {
647+
var answer = lhs
648+
answer *= rhs
649+
return answer
650+
}
651+
652+
public static func /(lhs: Decimal, rhs: Decimal) -> Decimal {
653+
var answer = lhs
654+
answer /= rhs
655+
return answer
656+
}
657+
658+
public mutating func negate() {
659+
guard self._length != 0 else { return }
660+
self._isNegative = self._isNegative == 0 ? 1 : 0
661+
}
662+
}
663+
664+
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
665+
extension Decimal : Strideable {
666+
public func distance(to other: Decimal) -> Decimal {
667+
return self - other
668+
}
669+
670+
public func advanced(by n: Decimal) -> Decimal {
671+
return self + n
672+
}
519673
}

0 commit comments

Comments
 (0)