Skip to content

Implement Decimal .int64Value and .uint64Value SPIs #749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions Sources/FoundationEssentials/Decimal/Decimal+Conformances.swift
Original file line number Diff line number Diff line change
Expand Up @@ -458,28 +458,6 @@ extension Decimal: Hashable {
}
}

internal var doubleValue: Double {
if _length == 0 {
return _isNegative == 1 ? Double.nan : 0
}

var d = 0.0
for idx in (0..<min(_length, 8)).reversed() {
d = d * 65536 + Double(self[idx])
}

if _exponent < 0 {
for _ in _exponent..<0 {
d /= 10.0
}
} else {
for _ in 0..<_exponent {
d *= 10.0
}
}
return _isNegative != 0 ? -d : d
}

public func hash(into hasher: inout Hasher) {
// FIXME: This is a weak hash. We should rather normalize self to a
// canonical member of the exact same equivalence relation that
Expand Down
81 changes: 81 additions & 0 deletions Sources/FoundationEssentials/Decimal/Decimal+Math.swift
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,87 @@ extension Decimal {
}
}

// MARK: - Numeric Values
extension Decimal {
internal var doubleValue: Double {
if _length == 0 {
return _isNegative == 1 ? Double.nan : 0
}

var d = 0.0
for idx in (0..<min(_length, 8)).reversed() {
d = d * 65536 + Double(self[idx])
}

if _exponent < 0 {
for _ in _exponent..<0 {
d /= 10.0
}
} else {
for _ in 0..<_exponent {
d *= 10.0
}
}
return _isNegative != 0 ? -d : d
}

private var _unsignedInt64Value: UInt64 {
// Quick check if number if has too many zeros before decimal point or too many trailing zeros after decimal point.
// Log10 (2^64) ~ 19, log10 (2^128) ~ 38
if self._exponent < -38 || self._exponent > 20 {
return 0
}
if self._length == 0 || self.isZero || self.magnitude < (0 as Decimal) {
return 0
}
var value = self.significand

for _ in 0 ..< abs(self._exponent) {
if self._exponent < 0 {
if let result = try? value._divide(by: 10) {
value = result.result
}
} else {
if let result = try? value._multiply(byShort: 10) {
value = result
}
}
}
return UInt64(value._mantissa.3) << 48 | UInt64(value._mantissa.2) << 32 | UInt64(value._mantissa.1) << 16 | UInt64(value._mantissa.0)
}

internal var int64Value: Int64 {
let uint64Value = self._unsignedInt64Value
if self._isNegative > 0 {
if uint64Value == Int64.max.magnitude + 1 {
return Int64.min
}
if uint64Value <= Int64.max.magnitude {
var value = Int64(uint64Value)
value.negate()
return value
}
}
return Int64(bitPattern: uint64Value)
}

internal var uint64Value: UInt64 {
let value = self._unsignedInt64Value
if self._isNegative == 0 {
return value
}
if value == Int64.max.magnitude + 1 {
return UInt64(bitPattern: Int64.min)
}
if value <= Int64.max.magnitude {
var value = Int64(value)
value.negate()
return UInt64(bitPattern: value)
}
return value
}
}

// MARK: - Integer Mathmatics
extension Decimal {
typealias VariableLengthInteger = [UInt16]
Expand Down
23 changes: 23 additions & 0 deletions Tests/FoundationEssentialsTests/DecimalTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1025,4 +1025,27 @@ final class DecimalTests : XCTestCase {
XCTAssertNotEqual(x.nextDown, x)
XCTAssertNotEqual(x.nextUp, x)
}

func test_int64Value() {
XCTAssertEqual(Decimal(-1).int64Value, -1)
XCTAssertEqual(Decimal(0).int64Value, 0)
XCTAssertEqual(Decimal(1).int64Value, 1)
XCTAssertEqual(Decimal.nan.int64Value, 0)
XCTAssertEqual(Decimal(1e50).int64Value, 0)
XCTAssertEqual(Decimal(1e-50).int64Value, 0)

XCTAssertEqual(Decimal(UInt64.max).uint64Value, UInt64.max)
XCTAssertEqual((Decimal(UInt64.max) + 1).uint64Value, 0)
XCTAssertEqual(Decimal(Int64.max).int64Value, Int64.max)
XCTAssertEqual((Decimal(Int64.max) + 1 ).int64Value, Int64.min)
XCTAssertEqual((Decimal(Int64.max) + 1 ).uint64Value, UInt64(Int64.max) + 1)
XCTAssertEqual(Decimal(Int64.min).int64Value, Int64.min)

XCTAssertEqual(Decimal(Int.min).int64Value, Int64(Int.min))

let div3 = Decimal(10) / 3
XCTAssertEqual(div3.int64Value, 3)
let pi = Decimal(Double.pi)
XCTAssertEqual(pi.int64Value, 3)
}
}