Skip to content

Commit 79cb33f

Browse files
Azoystephentyrone
authored andcommitted
[SR-8178] Fix BinaryFloatingPoint.random(in:) open range returning upperBound (#17794)
1 parent b952876 commit 79cb33f

File tree

2 files changed

+61
-8
lines changed

2 files changed

+61
-8
lines changed

stdlib/public/core/FloatingPoint.swift.gyb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2461,7 +2461,13 @@ where Self.RawSignificand : FixedWidthInteger {
24612461
% end
24622462
}
24632463
let unitRandom = Self.init(rand) * Self.ulpOfOne / 2
2464-
return delta * unitRandom + range.lowerBound
2464+
let randFloat = delta * unitRandom + range.lowerBound
2465+
% if 'Closed' not in Range:
2466+
if randFloat == range.upperBound {
2467+
return Self.random(in: range, using: &generator)
2468+
}
2469+
% end
2470+
return randFloat
24652471
}
24662472

24672473
/// Returns a random value within the specified range.

validation-test/stdlib/Random.swift

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ RandomTests.test("basic random numbers") {
4343

4444
// Random integers in ranges
4545

46-
func integerRangeTest<T: FixedWidthInteger>(_ type: T.Type)
47-
where T.Stride: SignedInteger, T.Magnitude: UnsignedInteger {
46+
func integerRangeTest<T: FixedWidthInteger>(_ type: T.Type) {
4847

4948
func testRange(_ range: Range<T>, iterations: Int = 1_000) {
5049
var integerSet: Set<T> = []
@@ -104,10 +103,8 @@ RandomTests.test("random integers in ranges") {
104103
// Random floating points in ranges
105104

106105
func floatingPointRangeTest<T: BinaryFloatingPoint>(_ type: T.Type)
107-
where T.RawSignificand: FixedWidthInteger,
108-
T.RawSignificand.Stride: SignedInteger & FixedWidthInteger,
109-
T.RawSignificand.Magnitude: UnsignedInteger {
110-
106+
where T.RawSignificand: FixedWidthInteger {
107+
111108
let testRange = 0 ..< 1_000
112109

113110
// open range
@@ -230,6 +227,56 @@ RandomTests.test("different random number generators") {
230227
expectEqual(shufflePasses[0], shufflePasses[1])
231228
}
232229

230+
// Random floating points with max values (SR-8178)
231+
232+
var lcrng = LCRNG(seed: 1234567890)
233+
234+
public struct RotatingRNG: RandomNumberGenerator {
235+
public let rotation: [() -> UInt64] = [
236+
{ return .min },
237+
{ return .max },
238+
{ return lcrng.next() }
239+
]
240+
public var rotationIndex = 0
241+
242+
public mutating func next() -> UInt64 {
243+
if rotationIndex == rotation.count {
244+
rotationIndex = 0
245+
}
246+
247+
defer {
248+
rotationIndex += 1
249+
}
250+
251+
return rotation[rotationIndex]()
252+
}
253+
}
254+
255+
func floatingPointRangeMaxTest<T: BinaryFloatingPoint>(_ type: T.Type)
256+
where T.RawSignificand: FixedWidthInteger {
257+
258+
let testRange = 0 ..< 1_000
259+
260+
var rng = RotatingRNG()
261+
let ranges: [Range<T>] = [0 ..< 1, 1 ..< 2, 10 ..< 11, 0 ..< 10]
262+
for range in ranges {
263+
for _ in testRange {
264+
let random = T.random(in: range, using: &rng)
265+
expectTrue(range.contains(random))
266+
expectTrue(range.lowerBound <= random)
267+
expectTrue(random < range.upperBound)
268+
}
269+
}
270+
}
271+
272+
RandomTests.test("random floating point range maxes") {
273+
floatingPointRangeMaxTest(Float.self)
274+
floatingPointRangeMaxTest(Double.self)
275+
#if !os(Windows) && (arch(i386) || arch(x86_64))
276+
floatingPointRangeMaxTest(Float80.self)
277+
#endif
278+
}
279+
233280
// Uniform Distribution
234281

235282
func chi2Test(_ samples: [Double]) -> Bool {
@@ -248,7 +295,7 @@ func chi2Test(_ samples: [Double]) -> Bool {
248295

249296
if chi2 > cvHigh {
250297
return false
251-
}else {
298+
} else {
252299
return true
253300
}
254301
}

0 commit comments

Comments
 (0)