Skip to content

Commit f9f2fc3

Browse files
stephentyroneairspeedswift
authored andcommitted
Streamline integer-range randomElement. (#16501)
This is a small performance win; mainly I'm interested in simplifying the code so that there are fewer weird corners for bugs to creep in. Nonetheless, it seems to be about 5% faster with the (fast, dumb) LCG generator.
1 parent 4c756c1 commit f9f2fc3

File tree

1 file changed

+19
-28
lines changed

1 file changed

+19
-28
lines changed

stdlib/public/core/Integers.swift.gyb

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2566,40 +2566,31 @@ extension ${Range}
25662566
guard !isEmpty else {
25672567
return nil
25682568
}
2569-
let isLowerNegative = Bound.isSigned && lowerBound < 0
2570-
let sameSign = !Bound.isSigned || isLowerNegative == (upperBound < 0)
2571-
% if 'Closed' not in Range:
2572-
let delta: Bound.Magnitude
2573-
% else:
2574-
var delta: Bound.Magnitude
2575-
% end
2576-
if isLowerNegative {
2577-
delta = sameSign
2578-
? lowerBound.magnitude - upperBound.magnitude
2579-
: lowerBound.magnitude + upperBound.magnitude
2580-
} else {
2581-
delta = upperBound.magnitude - lowerBound.magnitude
2582-
}
2569+
// Compute delta, the distance between the lower and upper bounds. This
2570+
// value may not representable by the type Bound if Bound is signed, but
2571+
// is always representable as Bound.Magnitude.
2572+
var delta = Bound.Magnitude(truncatingIfNeeded: upperBound &- lowerBound)
25832573
% if 'Closed' in Range:
2574+
// Subtle edge case: if the range is the whole set of representable values,
2575+
// then adding one to delta to account for a closed range will overflow.
2576+
// If we used &+ instead, the result would be zero, which isn't helpful,
2577+
// so we actually need to handle this case separately.
25842578
if delta == Bound.Magnitude.max {
25852579
return Bound(truncatingIfNeeded: generator.next() as Bound.Magnitude)
25862580
}
2581+
// Need to widen delta to account for the right-endpoint of a closed range.
25872582
delta += 1
25882583
% end
2589-
let randomMagnitude = generator.next(upperBound: delta)
2590-
if sameSign {
2591-
return lowerBound + Bound(randomMagnitude)
2592-
} else {
2593-
% if 'Closed' not in Range:
2594-
return randomMagnitude < upperBound.magnitude
2595-
? Bound(randomMagnitude)
2596-
: -1 - Bound(randomMagnitude - upperBound.magnitude)
2597-
% else:
2598-
return Bound.isSigned && randomMagnitude <= upperBound.magnitude
2599-
? Bound(randomMagnitude)
2600-
: 0 - Bound(randomMagnitude - upperBound.magnitude)
2601-
% end
2602-
}
2584+
// The mathematical result we want is lowerBound plus a random value in
2585+
// 0 ..< delta. We need to be slightly careful about how we do this
2586+
// arithmetic; the Bound type cannot generally represent the random value,
2587+
// so we use a wrapping addition on Bound.Magnitude. This will often
2588+
// overflow, but produces the correct bit pattern for the result when
2589+
// converted back to Bound.
2590+
return Bound(truncatingIfNeeded:
2591+
Bound.Magnitude(truncatingIfNeeded: lowerBound) &+
2592+
generator.next(upperBound: delta)
2593+
)
26032594
}
26042595
26052596
/// Returns a random element of the range, using the given generator as

0 commit comments

Comments
 (0)