1010//
1111//===----------------------------------------------------------------------===//
1212
13+ /// A numeric type that uses two Double values as its representation, providing
14+ /// about 106 bits of precision with the same exponent range as Double.
15+ ///
16+ /// This type conforms to AdditiveArithmetic, Hashable and Comparable, but does
17+ /// not conform to FloatingPoint or Numeric; it implements only the API surface
18+ /// that is necessary to serve as an internal implementation detail of Date.
1319internal struct DoubleDouble {
1420
1521 private let storage : ( Double , Double )
1622
23+ /// A double-double value constructed by specifying the head and tail.
24+ ///
25+ /// This is an unchecked operation because it does not enforce the
26+ /// invariant that head + tail == head in release builds, which is
27+ /// necessary for subsequent arithmetic operations to behave correctly.
1728 @_transparent
18- init ( head: Double , tail: Double ) {
29+ init ( uncheckedHead head: Double , tail: Double ) {
30+ assert ( !head. isFinite || head + tail == head)
1931 storage = ( head, tail)
2032 }
2133
34+ /// The high-order Double.
35+ ///
36+ /// This property does not have a setter because `head` should pretty much
37+ /// never be set independently of `tail`, so as to maintain the invariant
38+ /// that `head + tail == head`. You can use `init(uncheckedHead:tail:)`
39+ /// to directly construct DoubleDouble values, which will enforce the
40+ /// invariant in debug builds.
2241 @_transparent
2342 var head : Double { storage. 0 }
2443
44+ /// The low-order Double.
45+ ///
46+ /// This property does not have a setter because `tail` should pretty much
47+ /// never be set independently of `head`, so as to maintain the invariant
48+ /// that `head + tail == head`. You can use `init(uncheckedHead:tail:)`
49+ /// to directly construct DoubleDouble values, which will enforce the
50+ /// invariant in debug builds.
2551 @_transparent
2652 var tail : Double { storage. 1 }
2753
54+ /// `a + b` represented as a normalized DoubleDouble.
55+ ///
56+ /// Computed via the [2Sum algorithm](https://en.wikipedia.org/wiki/2Sum).
2857 @inlinable
2958 static func sum( _ a: Double , _ b: Double ) -> DoubleDouble {
3059 let head = a + b
3160 let x = head - b
3261 let y = head - x
3362 let tail = ( a - x) + ( b - y)
34- return DoubleDouble ( head : head, tail: tail)
63+ return DoubleDouble ( uncheckedHead : head, tail: tail)
3564 }
3665
66+ /// `a + b` represented as a normalized DoubleDouble.
67+ ///
68+ /// Computed via the [Fast2Sum algorithm](https://en.wikipedia.org/wiki/2Sum).
69+ ///
70+ /// - Precondition:
71+ /// `large` and `small` must be such that `sum(large:small:)`
72+ /// produces the same result as `sum(_:_:)` would. A sufficient condition
73+ /// is that `|large| >= |small|`, but this is not necessary, so we do not
74+ /// enforce it via an assert. Instead this function asserts that the result
75+ /// is the same as that produced by `sum(_:_:)` in Debug builds. This is
76+ /// unchecked in Release.
3777 @inlinable
3878 static func sum( large a: Double , small b: Double ) -> DoubleDouble {
3979 let head = a + b
4080 let tail = a - head + b
41- return DoubleDouble ( head: head, tail: tail)
81+ let result = DoubleDouble ( uncheckedHead: head, tail: tail)
82+ assert ( !head. isFinite || result == sum ( a, b) )
83+ return result
4284 }
4385
86+ /// `a * b` represented as a normalized DoubleDouble.
4487 @inlinable
4588 static func product( _ a: Double , _ b: Double ) -> DoubleDouble {
4689 let head = a * b
4790 let tail = ( - head) . addingProduct ( a, b)
48- return DoubleDouble ( head : head, tail: tail)
91+ return DoubleDouble ( uncheckedHead : head, tail: tail)
4992 }
5093}
5194
@@ -72,7 +115,7 @@ extension DoubleDouble: Hashable {
72115extension DoubleDouble : AdditiveArithmetic {
73116 @inlinable
74117 static var zero : DoubleDouble {
75- Self ( head : 0 , tail: 0 )
118+ Self ( uncheckedHead : 0 , tail: 0 )
76119 }
77120
78121 @inlinable
@@ -83,6 +126,8 @@ extension DoubleDouble: AdditiveArithmetic {
83126 return sum ( large: first. head, small: first. tail + tails. tail)
84127 }
85128
129+ /// Equivalent to `a + DoubleDouble(uncheckedHead: b, tail: 0)` but
130+ /// computed more efficiently.
86131 @inlinable
87132 static func + ( a: DoubleDouble , b: Double ) -> DoubleDouble {
88133 let heads = sum ( a. head, b)
@@ -92,14 +137,16 @@ extension DoubleDouble: AdditiveArithmetic {
92137
93138 @inlinable
94139 prefix static func - ( a: DoubleDouble ) -> DoubleDouble {
95- DoubleDouble ( head : - a. head, tail: - a. tail)
140+ DoubleDouble ( uncheckedHead : - a. head, tail: - a. tail)
96141 }
97142
98143 @inlinable
99144 static func - ( a: DoubleDouble , b: DoubleDouble ) -> DoubleDouble {
100145 a + ( - b)
101146 }
102147
148+ /// Equivalent to `a - DoubleDouble(uncheckedHead: b, tail: 0)` but
149+ /// computed more efficiently.
103150 @inlinable
104151 static func - ( a: DoubleDouble , b: Double ) -> DoubleDouble {
105152 a + ( - b)
@@ -111,7 +158,7 @@ extension DoubleDouble {
111158 static func * ( a: DoubleDouble , b: Double ) -> DoubleDouble {
112159 let tmp = product ( a. head, b)
113160 return DoubleDouble (
114- head : tmp. head,
161+ uncheckedHead : tmp. head,
115162 tail: tmp. tail. addingProduct ( a. tail, b)
116163 )
117164 }
@@ -120,17 +167,20 @@ extension DoubleDouble {
120167 static func / ( a: DoubleDouble , b: Double ) -> DoubleDouble {
121168 let head = a. head/ b
122169 let residual = a. head. addingProduct ( - head, b) + a. tail
123- return DoubleDouble ( head : head, tail : residual/ b)
170+ return . sum ( large : head, small : residual/ b)
124171 }
125172}
126173
127174extension DoubleDouble {
175+ // This value rounded down to an integer.
128176 @inlinable
129177 func floor( ) -> DoubleDouble {
130178 let approx = head. rounded ( . down)
179+ // If head was already an integer, round tail down and renormalize.
131180 if approx == head {
132181 return . sum( large: head, small: tail. rounded ( . down) )
133182 }
134- return DoubleDouble ( head: approx, tail: 0 )
183+ // Head was not an integer; we can simply discard tail.
184+ return DoubleDouble ( uncheckedHead: approx, tail: 0 )
135185 }
136186}
0 commit comments