Skip to content

Commit 9109dde

Browse files
authored
Implement Decimal .int64Value and .uint64Value SPIs (#749)
These SPIs will be used by swift-corelibs-foundation resolves: rdar://132050472
1 parent 2b6543f commit 9109dde

File tree

3 files changed

+104
-22
lines changed

3 files changed

+104
-22
lines changed

Sources/FoundationEssentials/Decimal/Decimal+Conformances.swift

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -458,28 +458,6 @@ extension Decimal: Hashable {
458458
}
459459
}
460460

461-
internal var doubleValue: Double {
462-
if _length == 0 {
463-
return _isNegative == 1 ? Double.nan : 0
464-
}
465-
466-
var d = 0.0
467-
for idx in (0..<min(_length, 8)).reversed() {
468-
d = d * 65536 + Double(self[idx])
469-
}
470-
471-
if _exponent < 0 {
472-
for _ in _exponent..<0 {
473-
d /= 10.0
474-
}
475-
} else {
476-
for _ in 0..<_exponent {
477-
d *= 10.0
478-
}
479-
}
480-
return _isNegative != 0 ? -d : d
481-
}
482-
483461
public func hash(into hasher: inout Hasher) {
484462
// FIXME: This is a weak hash. We should rather normalize self to a
485463
// canonical member of the exact same equivalence relation that

Sources/FoundationEssentials/Decimal/Decimal+Math.swift

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,87 @@ extension Decimal {
623623
}
624624
}
625625

626+
// MARK: - Numeric Values
627+
extension Decimal {
628+
internal var doubleValue: Double {
629+
if _length == 0 {
630+
return _isNegative == 1 ? Double.nan : 0
631+
}
632+
633+
var d = 0.0
634+
for idx in (0..<min(_length, 8)).reversed() {
635+
d = d * 65536 + Double(self[idx])
636+
}
637+
638+
if _exponent < 0 {
639+
for _ in _exponent..<0 {
640+
d /= 10.0
641+
}
642+
} else {
643+
for _ in 0..<_exponent {
644+
d *= 10.0
645+
}
646+
}
647+
return _isNegative != 0 ? -d : d
648+
}
649+
650+
private var _unsignedInt64Value: UInt64 {
651+
// Quick check if number if has too many zeros before decimal point or too many trailing zeros after decimal point.
652+
// Log10 (2^64) ~ 19, log10 (2^128) ~ 38
653+
if self._exponent < -38 || self._exponent > 20 {
654+
return 0
655+
}
656+
if self._length == 0 || self.isZero || self.magnitude < (0 as Decimal) {
657+
return 0
658+
}
659+
var value = self.significand
660+
661+
for _ in 0 ..< abs(self._exponent) {
662+
if self._exponent < 0 {
663+
if let result = try? value._divide(by: 10) {
664+
value = result.result
665+
}
666+
} else {
667+
if let result = try? value._multiply(byShort: 10) {
668+
value = result
669+
}
670+
}
671+
}
672+
return UInt64(value._mantissa.3) << 48 | UInt64(value._mantissa.2) << 32 | UInt64(value._mantissa.1) << 16 | UInt64(value._mantissa.0)
673+
}
674+
675+
internal var int64Value: Int64 {
676+
let uint64Value = self._unsignedInt64Value
677+
if self._isNegative > 0 {
678+
if uint64Value == Int64.max.magnitude + 1 {
679+
return Int64.min
680+
}
681+
if uint64Value <= Int64.max.magnitude {
682+
var value = Int64(uint64Value)
683+
value.negate()
684+
return value
685+
}
686+
}
687+
return Int64(bitPattern: uint64Value)
688+
}
689+
690+
internal var uint64Value: UInt64 {
691+
let value = self._unsignedInt64Value
692+
if self._isNegative == 0 {
693+
return value
694+
}
695+
if value == Int64.max.magnitude + 1 {
696+
return UInt64(bitPattern: Int64.min)
697+
}
698+
if value <= Int64.max.magnitude {
699+
var value = Int64(value)
700+
value.negate()
701+
return UInt64(bitPattern: value)
702+
}
703+
return value
704+
}
705+
}
706+
626707
// MARK: - Integer Mathmatics
627708
extension Decimal {
628709
typealias VariableLengthInteger = [UInt16]

Tests/FoundationEssentialsTests/DecimalTests.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,4 +1207,27 @@ final class DecimalTests : XCTestCase {
12071207
XCTAssertNotEqual(x.nextDown, x)
12081208
XCTAssertNotEqual(x.nextUp, x)
12091209
}
1210+
1211+
func test_int64Value() {
1212+
XCTAssertEqual(Decimal(-1).int64Value, -1)
1213+
XCTAssertEqual(Decimal(0).int64Value, 0)
1214+
XCTAssertEqual(Decimal(1).int64Value, 1)
1215+
XCTAssertEqual(Decimal.nan.int64Value, 0)
1216+
XCTAssertEqual(Decimal(1e50).int64Value, 0)
1217+
XCTAssertEqual(Decimal(1e-50).int64Value, 0)
1218+
1219+
XCTAssertEqual(Decimal(UInt64.max).uint64Value, UInt64.max)
1220+
XCTAssertEqual((Decimal(UInt64.max) + 1).uint64Value, 0)
1221+
XCTAssertEqual(Decimal(Int64.max).int64Value, Int64.max)
1222+
XCTAssertEqual((Decimal(Int64.max) + 1 ).int64Value, Int64.min)
1223+
XCTAssertEqual((Decimal(Int64.max) + 1 ).uint64Value, UInt64(Int64.max) + 1)
1224+
XCTAssertEqual(Decimal(Int64.min).int64Value, Int64.min)
1225+
1226+
XCTAssertEqual(Decimal(Int.min).int64Value, Int64(Int.min))
1227+
1228+
let div3 = Decimal(10) / 3
1229+
XCTAssertEqual(div3.int64Value, 3)
1230+
let pi = Decimal(Double.pi)
1231+
XCTAssertEqual(pi.int64Value, 3)
1232+
}
12101233
}

0 commit comments

Comments
 (0)