Skip to content

Commit 63c36df

Browse files
authored
Merge pull request #2943 from xwu/floating-point-stride
2 parents efb4b54 + 6a4da3f commit 63c36df

File tree

2 files changed

+108
-22
lines changed

2 files changed

+108
-22
lines changed

stdlib/public/core/Stride.swift.gyb

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ public protocol ${Self} : ${Conformance} {
4444
/// - Complexity: O(1).
4545
func advanced(by n: Stride) -> Self
4646

47+
/// `_step` is an implementation detail of Strideable; do not use it directly.
48+
static func _step(
49+
after current: (index: Int?, value: Self),
50+
from start: Self, by distance: Self.Stride
51+
) -> (index: Int?, value: Self)
52+
4753
associatedtype _DisabledRangeIndex = _DisabledRangeIndex_
4854
}
4955

@@ -125,22 +131,54 @@ public func -= <T : UnsignedInteger>(
125131

126132
//===----------------------------------------------------------------------===//
127133

128-
/// An iterator for the result of `stride(from:to:)`.
134+
extension Strideable {
135+
public static func _step(
136+
after current: (index: Int?, value: Self),
137+
from start: Self, by distance: Self.Stride
138+
) -> (index: Int?, value: Self) {
139+
return (nil, current.value + distance)
140+
}
141+
}
142+
143+
extension Strideable where Stride : FloatingPoint {
144+
public static func _step(
145+
after current: (index: Int?, value: Self),
146+
from start: Self, by distance: Self.Stride
147+
) -> (index: Int?, value: Self) {
148+
if let i = current.index {
149+
return (i + 1, start + Stride(i + 1) * distance)
150+
}
151+
// If current.index == nil, either we're just starting out (in which case
152+
// the next index is 1), or we should proceed without an index just as
153+
// though this floating point specialization doesn't exist.
154+
return (current.value == start ? 1 : nil, current.value + distance)
155+
}
156+
}
157+
158+
/// An iterator for `StrideTo<Element>`.
129159
public struct StrideToIterator<Element : Strideable> : IteratorProtocol {
130-
internal var _current: Element
160+
internal let _start: Element
131161
internal let _end: Element
132162
internal let _stride: Element.Stride
163+
internal var _current: (index: Int?, value: Element)
164+
165+
internal init(_start: Element, end: Element, stride: Element.Stride) {
166+
self._start = _start
167+
_end = end
168+
_stride = stride
169+
_current = (nil, _start)
170+
}
133171

134172
/// Advances to the next element and returns it, or `nil` if no next element
135173
/// exists.
136174
///
137175
/// Once `nil` has been returned, all subsequent calls return `nil`.
138176
public mutating func next() -> Element? {
139-
if _stride > 0 ? _current >= _end : _current <= _end {
177+
let result = _current.value
178+
if _stride > 0 ? result >= _end : result <= _end {
140179
return nil
141180
}
142-
let result = _current
143-
_current += _stride
181+
_current = Element._step(after: _current, from: _start, by: _stride)
144182
return result
145183
}
146184
}
@@ -153,12 +191,12 @@ public struct StrideTo<Element : Strideable> : Sequence, CustomReflectable {
153191
///
154192
/// - Complexity: O(1).
155193
public func makeIterator() -> StrideToIterator<Element> {
156-
return StrideToIterator(_current: _start, _end: _end, _stride: _stride)
194+
return StrideToIterator(_start: _start, end: _end, stride: _stride)
157195
}
158196

159197
internal init(_start: Element, end: Element, stride: Element.Stride) {
160198
_precondition(stride != 0, "stride size must not be zero")
161-
// Unreachable endpoints are allowed; they just make for an
199+
// At start, striding away from end is allowed; it just makes for an
162200
// already-empty Sequence.
163201
self._start = _start
164202
self._end = end
@@ -175,21 +213,29 @@ public struct StrideTo<Element : Strideable> : Sequence, CustomReflectable {
175213
}
176214

177215
/// Returns the sequence of values (`self`, `self + stride`, `self +
178-
/// stride + stride`, ... *last*) where *last* is the last value in
179-
/// the progression that is less than `end`.
216+
/// 2 * stride`, ... *last*) where *last* is the last value in the
217+
/// progression that is less than `end`.
180218
public func stride<T : Strideable>(
181219
from start: T, to end: T, by stride: T.Stride
182220
) -> StrideTo<T> {
183221
return StrideTo(_start: start, end: end, stride: stride)
184222
}
185223

186-
/// An `IteratorProtocol` for `StrideThrough<Element>`.
224+
/// An iterator for `StrideThrough<Element>`.
187225
public struct StrideThroughIterator<Element : Strideable> : IteratorProtocol {
188-
internal var _current: Element
226+
internal let _start: Element
189227
internal let _end: Element
190228
internal let _stride: Element.Stride
229+
internal var _current: (index: Int?, value: Element)
191230
internal var _done: Bool = false
192231

232+
internal init(_start: Element, end: Element, stride: Element.Stride) {
233+
self._start = _start
234+
_end = end
235+
_stride = stride
236+
_current = (nil, _start)
237+
}
238+
193239
/// Advances to the next element and returns it, or `nil` if no next element
194240
/// exists.
195241
///
@@ -198,15 +244,15 @@ public struct StrideThroughIterator<Element : Strideable> : IteratorProtocol {
198244
if _done {
199245
return nil
200246
}
201-
if _stride > 0 ? _current >= _end : _current <= _end {
202-
if _current == _end {
247+
let result = _current.value
248+
if _stride > 0 ? result >= _end : result <= _end {
249+
if result == _end {
203250
_done = true
204-
return _current
251+
return result
205252
}
206253
return nil
207254
}
208-
let result = _current
209-
_current += _stride
255+
_current = Element._step(after: _current, from: _start, by: _stride)
210256
return result
211257
}
212258
}
@@ -221,8 +267,7 @@ public struct StrideThrough<
221267
///
222268
/// - Complexity: O(1).
223269
public func makeIterator() -> StrideThroughIterator<Element> {
224-
return StrideThroughIterator(
225-
_current: _start, _end: _end, _stride: _stride, _done: false)
270+
return StrideThroughIterator(_start: _start, end: _end, stride: _stride)
226271
}
227272

228273
internal init(_start: Element, end: Element, stride: Element.Stride) {
@@ -243,8 +288,8 @@ public struct StrideThrough<
243288
}
244289

245290
/// Returns the sequence of values (`self`, `self + stride`, `self +
246-
/// stride + stride`, ... *last*) where *last* is the last value in
247-
/// the progression less than or equal to `end`.
291+
/// 2 * stride`, ... *last*) where *last* is the last value in the
292+
/// progression less than or equal to `end`.
248293
///
249294
/// - Note: There is no guarantee that `end` is an element of the sequence.
250295
public func stride<T : Strideable>(

test/1_stdlib/Strideable.swift

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,41 @@ struct R : Strideable {
6262
}
6363

6464
StrideTestSuite.test("Double") {
65-
// Doubles are not yet ready for testing, since they still conform
66-
// to RandomAccessIndex
65+
func checkOpen(from start: Double, to end: Double, by stepSize: Double, sum: Double) {
66+
// Work on Doubles
67+
expectEqual(
68+
sum,
69+
stride(from: start, to: end, by: stepSize).reduce(0.0, combine: +))
70+
}
71+
72+
func checkClosed(from start: Double, through end: Double, by stepSize: Double, sum: Double) {
73+
// Work on Doubles
74+
expectEqual(
75+
sum,
76+
stride(from: start, through: end, by: stepSize).reduce(0.0, combine: +))
77+
}
78+
79+
checkOpen(from: 1.0, to: 15.0, by: 3.0, sum: 35.0)
80+
checkOpen(from: 1.0, to: 16.0, by: 3.0, sum: 35.0)
81+
checkOpen(from: 1.0, to: 17.0, by: 3.0, sum: 51.0)
82+
83+
checkOpen(from: 1.0, to: -13.0, by: -3.0, sum: -25.0)
84+
checkOpen(from: 1.0, to: -14.0, by: -3.0, sum: -25.0)
85+
checkOpen(from: 1.0, to: -15.0, by: -3.0, sum: -39.0)
86+
87+
checkOpen(from: 4.0, to: 16.0, by: -3.0, sum: 0.0)
88+
checkOpen(from: 1.0, to: -16.0, by: 3.0, sum: 0.0)
89+
90+
checkClosed(from: 1.0, through: 15.0, by: 3.0, sum: 35.0)
91+
checkClosed(from: 1.0, through: 16.0, by: 3.0, sum: 51.0)
92+
checkClosed(from: 1.0, through: 17.0, by: 3.0, sum: 51.0)
93+
94+
checkClosed(from: 1.0, through: -13.0, by: -3.0, sum: -25.0)
95+
checkClosed(from: 1.0, through: -14.0, by: -3.0, sum: -39.0)
96+
checkClosed(from: 1.0, through: -15.0, by: -3.0, sum: -39.0)
97+
98+
checkClosed(from: 4.0, through: 16.0, by: -3.0, sum: 0.0)
99+
checkClosed(from: 1.0, through: -16.0, by: 3.0, sum: 0.0)
67100
}
68101

69102
StrideTestSuite.test("HalfOpen") {
@@ -163,5 +196,13 @@ StrideTestSuite.test("FloatingPointStride") {
163196
expectEqual([ 1.4, 2.4, 3.4 ], result)
164197
}
165198

199+
StrideTestSuite.test("ErrorAccumulation") {
200+
let a = Array(stride(from: Float(1.0), through: Float(2.0), by: Float(0.1)))
201+
expectEqual(11, a.count)
202+
expectEqual(Float(2.0), a.last)
203+
let b = Array(stride(from: Float(1.0), to: Float(10.0), by: Float(0.9)))
204+
expectEqual(10, b.count)
205+
}
206+
166207
runAllTests()
167208

0 commit comments

Comments
 (0)