Skip to content

Commit dd56edf

Browse files
ph1psstephentyrone
andauthored
Expose attosecond representation of Duration (#2633)
* Add proposal for `Int128` support in `Duration` * Add PR to NNNN-duration-and-int128.md * Rename proposal to be more clear about the intent * Update and rename NNNN-duration-attosecond-represenation.md to 0457-duration-attosecond-represenation.md Assigned SE-0457 --------- Co-authored-by: Stephen Canon <[email protected]>
1 parent a890bd2 commit dd56edf

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Expose attosecond representation of `Duration`
2+
3+
* Proposal: [SE-0457](0457-duration-attosecond-represenation.md)
4+
* Authors: [Philipp Gabriel](https://github.com/ph1ps)
5+
* Review Manager: [Stephen Canon](https://github.com/stephentyrone)
6+
* Status: **Active review (January 16 ... 30, 2025)**
7+
* Implementation: [swiftlang/swift#78202](https://github.com/swiftlang/swift/pull/78202)
8+
* Review: ([pitch](https://forums.swift.org/t/pitch-adding-int128-support-to-duration))
9+
10+
## Introduction
11+
This proposal introduces public APIs to enable seamless integration of `Int128` into the `Duration` type. Specifically, it provides support for directly accessing a `Duration`'s attosecond representation via the newly available `Int128` type and simplifies the creation of `Duration` values from attoseconds.
12+
13+
## Motivation
14+
The `Duration` type currently offers two ways to construct and decompose itself:
15+
16+
**Low and high bits**
17+
```swift
18+
public struct Duration: Sendable {
19+
public var _low: UInt64
20+
public var _high: Int64
21+
public init(_high: Int64, low: UInt64) { ... }
22+
}
23+
```
24+
**Components**
25+
```swift
26+
extension Duration {
27+
public var components: (seconds: Int64, attoseconds: Int64) { ... }
28+
public init(secondsComponent: Int64, attosecondsComponent: Int64) { ... }
29+
}
30+
```
31+
However, both approaches have limitations when it comes to exposing `Duration`'s total attosecond representation:
32+
- The `_low` and `_high` properties are underscored, indicating that their direct use is discouraged.
33+
- The `components` property decomposes the value into seconds and attoseconds, requiring additional arithmetic operations for many use cases.
34+
35+
This gap becomes particularly evident in scenarios like generating a random `Duration`, which currently requires verbose and potentially inefficient code:
36+
```swift
37+
func randomDuration(upTo maxDuration: Duration) -> Duration {
38+
let attosecondsPerSecond: Int128 = 1_000_000_000_000_000_000
39+
let upperRange = Int128(maxDuration.components.seconds) * attosecondsPerSecond + Int128(maxDuration.components.attoseconds)
40+
let (seconds, attoseconds) = Int128.random(in: 0..<upperRange).quotientAndRemainder(dividingBy: attosecondsPerSecond)
41+
return .init(secondsComponent: Int64(seconds), attosecondsComponent: Int64(attoseconds))
42+
}
43+
```
44+
45+
By introducing direct `Int128` support to `Duration`, this complexity is eliminated. Developers can write concise and efficient code instead:
46+
```swift
47+
func randomDuration(upTo maxDuration: Duration) -> Duration {
48+
return Duration(attoseconds: Int128.random(in: 0..<maxDuration.attoseconds))
49+
}
50+
```
51+
This addition reduces boilerplate, minimizes potential errors, and improves performance for use cases requiring high-precision time calculations.
52+
53+
## Proposed solution
54+
This proposal complements the existing construction and decomposition options by introducing a third approach, leveraging the new `Int128` type:
55+
56+
- A new computed property `attoseconds`, which exposes the total attoseconds of a `Duration` as an `Int128`.
57+
- A new initializer `init(attoseconds: Int128)`, which allows creating a `Duration` directly from a single 128-bit value.
58+
59+
These additions provide a direct and efficient mechanism for working with `Duration` values while maintaining full compatibility with existing APIs.
60+
61+
## Detailed design
62+
Internally, the `Duration` type represents its value using the underscored `_high` and `_low` properties, which encode attoseconds as a 128-bit integer split into two 64-bit values. The proposed APIs unify these components into a single `Int128` representation:
63+
```swift
64+
@available(SwiftStdlib 6.0, *)
65+
extension Duration {
66+
/// The duration represented in attoseconds.
67+
public var attoseconds: Int128 {
68+
Int128(_low: _low, _high: _high)
69+
}
70+
71+
/// Initializes a `Duration` from the given number of attoseconds.
72+
public init(attoseconds: Int128) {
73+
self.init(_high: attoseconds._high, low: attoseconds._low)
74+
}
75+
}
76+
```
77+
78+
## Source compatibility
79+
This proposal is additive and source-compatible with existing code.
80+
81+
## ABI compatibility
82+
This proposal is additive and ABI-compatible with existing code.
83+
84+
## Implications on adoption
85+
The additions described in this proposal require a new version of the standard library.
86+
87+
## Alternatives considered
88+
### Static factory instead of or in addtion to initializer
89+
An alternative approach to the proposed `init(attoseconds:)` initializer is a static factory method. This design aligns with existing methods like `nanoseconds`, `microseconds`, etc., and provides a consistent naming pattern for creating `Duration` values.
90+
91+
```swift
92+
@available(SwiftStdlib 6.0, *)
93+
extension Duration {
94+
public static func attoseconds(_ attoseconds: Int128) -> Duration { ... }
95+
}
96+
```
97+
98+
However, this approach would introduce asymmetry to other factory methods which support both `Double` and `BinaryInteger` overloads:
99+
```swift
100+
extension Duration {
101+
public static func microseconds<T: BinaryInteger>(_ microseconds: T) -> Duration { ... }
102+
public static func microseconds(_ microseconds: Double) -> Duration { ... }
103+
}
104+
```
105+
For attoseconds, adding these overloads would lead to practical issues:
106+
107+
1. A `Double` overload is nonsensical because sub-attoseconds are not supported, meaning the method cannot represent fractional attoseconds.
108+
2. A `BinaryInteger` overload introduces additional complexity. Since it would need to support types other than `Int128`, arithmetic operations would be necessary to ensure correct scaling and truncation, negating the simplicity and precision that the `Int128`-specific initializer aims to provide.
109+
110+
Ultimately, the static func `attoseconds(_:)` would likely end up as a one-off method with only an `Int128` overload. This inconsistency diminishes the appeal of the factory method approach. The proposed `init(attoseconds:)` initializer avoids these issues, offering a direct and clear way to work with attoseconds, while remaining symmetrical with the existing `Duration` API structure.

0 commit comments

Comments
 (0)