I'm trying to write a function which wraps an input value within an input range (inclusive lower-bound, exclusive upper-bound), so that the output value is always within that range.
For example, 4.9.wrapped(within: 3..<5) == 4.9
, and 5.wrapped(within: 3..<5) == 3
.
(3..<5
is Swift syntax for [3, 5)
).
I wrote the below algorithm, which almost works perfectly, except when the input value is less than the lower-bound of the input range, then the output's inclusivity flips.
For example, when the input range is [3, 5)
and the input value is 20
, then the range output correctly reflects as [3, 5)
in the output, but when the input value is 1
with the same input range, then the output incorrectly reflects a (3, 5]
range.
Example code in Swift
public extension FloatingPoint {
func wrapped(within range: Range<Self>) -> Self {
let breadth = range.upperBound - range.lowerBound
let offset: Self
if self < range.lowerBound {
offset = breadth
}
else {
offset = 0
}
let baseResult = (self - range.lowerBound).truncatingRemainder(dividingBy: breadth)
return baseResult range.lowerBound offset
}
}
Example code as pseudocode
function (value: Number, rangeLowerBoundInclusive: Number, rangeUpperBoundExclusive: Number) returns Number {
let breadth be (rangeUpperBoundExclusive - rangeLowerBoundInclusive)
if (value is less than rangeLowerBoundInclusive)
let offset be breadth
else
let offset be 0
let baseResult be ((value - rangeLowerBoundInclusive) mod breadth)
return baseResult rangeLowerBoundInclusive offset
}
Output Visualization
Here is a visualization of the output for all input values from -10
to 10
, stepping 0.1
at a time, with the range [3, 5)
:
As you can see, the input values less than 3.0
all appear within the range (3, 5]
, and the input values greater than or equal to 3.0
all appear within the range [3, 5)
. Why is this, and how can I make it so that all values are within [3, 5)
?
CodePudding user response:
The edge case is when self - lowerBound
is divisible by breadth
at left side of the range.
In this situation, baseResult
is 0
, your code will wrongly add offset of breadth
, then the result becomes 5
.
You want to return lowerBound baseResult breadth
only when baseResult < 0
(different from your code: self < range.lowerBound
).