Skip to content

Commit 4a89062

Browse files
Require .upperBound - .lowerBound be finite for FloatingPoint random (#17833)
This is a slightly conservative precondition; when we re-work the FloatingPoint random computation in a more principled fashion, we can relax this to only requiring that .upperBound and .lowerBound are both finite. However, the current computation will break down unless this conservative condition is used, and this is future proof--we will only relax it going forward.
1 parent 685f31b commit 4a89062

File tree

1 file changed

+16
-4
lines changed

1 file changed

+16
-4
lines changed

stdlib/public/core/FloatingPoint.swift.gyb

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,7 +2411,7 @@ where Self.RawSignificand : FixedWidthInteger {
24112411
/// - Parameters:
24122412
/// - range: The range in which to create a random value.
24132413
% if Range == 'Range':
2414-
/// `range` must not be empty.
2414+
/// `range` must not be empty and finite.
24152415
% end
24162416
/// - generator: The random number generator to use when creating the
24172417
/// new random value.
@@ -2426,6 +2426,15 @@ where Self.RawSignificand : FixedWidthInteger {
24262426
"Can't get random value with an empty range"
24272427
)
24282428
let delta = range.upperBound - range.lowerBound
2429+
// TODO: this still isn't quite right, because the computation of delta
2430+
// can overflow (e.g. if .upperBound = .maximumFiniteMagnitude and
2431+
// .lowerBound = -.upperBound); this should be re-written with an
2432+
// algorithm that handles that case correctly, but this precondition
2433+
// is an acceptable short-term fix.
2434+
_precondition(
2435+
delta.isFinite,
2436+
"There is no uniform distribution on an infinite range"
2437+
)
24292438
let rand: Self.RawSignificand
24302439
if Self.RawSignificand.bitWidth == Self.significandBitCount + 1 {
24312440
rand = generator.next()
@@ -2439,15 +2448,18 @@ where Self.RawSignificand : FixedWidthInteger {
24392448
let significandCount = Self.significandBitCount + 1
24402449
let maxSignificand: Self.RawSignificand = 1 << significandCount
24412450
% if 'Closed' not in Range:
2442-
rand = generator.next(upperBound: maxSignificand)
2451+
// Rather than use .next(upperBound:), which has to work with arbitrary
2452+
// upper bounds, and therefore does extra work to avoid bias, we can take
2453+
// a shortcut because we know that maxSignificand is a power of two.
2454+
rand = generator.next() & (maxSignificand - 1)
24432455
% else:
24442456
rand = generator.next(upperBound: maxSignificand + 1)
24452457
if rand == maxSignificand {
24462458
return range.upperBound
24472459
}
24482460
% end
24492461
}
2450-
let unitRandom = Self.init(rand) * Self.ulpOfOne / 2
2462+
let unitRandom = Self.init(rand) * (Self.ulpOfOne / 2)
24512463
let randFloat = delta * unitRandom + range.lowerBound
24522464
% if 'Closed' not in Range:
24532465
if randFloat == range.upperBound {
@@ -2482,7 +2494,7 @@ where Self.RawSignificand : FixedWidthInteger {
24822494
///
24832495
/// - Parameter range: The range in which to create a random value.
24842496
% if Range == 'Range':
2485-
/// `range` must not be empty.
2497+
/// `range` must not be empty and finite.
24862498
% end
24872499
/// - Returns: A random value within the bounds of `range`.
24882500
@inlinable

0 commit comments

Comments
 (0)