Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upAdd clamp RFC #1961
Conversation
Xaeroxe
added some commits
Mar 27, 2017
This comment has been minimized.
This comment has been minimized.
alexcrichton
added
the
T-libs
label
Mar 27, 2017
kennytm
reviewed
Mar 27, 2017
| /// Returns the upper bound of the range if input is greater than the range, and the lower bound of | ||
| /// the range if input is less than the range. Otherwise this will return input. | ||
| #[inline] | ||
| pub fn clamp<T: Ord>(input: T, range: RangeInclusive<T>) -> T { |
This comment has been minimized.
This comment has been minimized.
kennytm
Mar 27, 2017
Member
I'm not sure if we should tie the stability of this RFC with #1192 (rust-lang/rust#28237) here by using range: RangeInclusive<T> instead of min: T, max: T.
This comment has been minimized.
This comment has been minimized.
Xaeroxe
Mar 27, 2017
Author
Contributor
After messing around with the playground a bit and doing some thinking I actually agree with you. I much prefer the RangeInclusive syntax but the instability of it is too much of a drawback.
Xaeroxe
referenced this pull request
Mar 27, 2017
Closed
Tracking issue for `..=` inclusive ranges (RFC #1192) -- originally `...` #28237
This comment has been minimized.
This comment has been minimized.
|
My two concerns:
|
This comment has been minimized.
This comment has been minimized.
bluetech
commented
Mar 28, 2017
|
I think the RFC and docstring should explicitly describe what happens if |
This comment has been minimized.
This comment has been minimized.
|
@pornel my first thought is that we ought to optimize the @bluetech agreed, I'll add something for that as soon as I can. |
This comment has been minimized.
This comment has been minimized.
|
@Xaeroxe please note that I think use of SSE dictates that if any of the operands is NaN, then NaN is returned. http://www.felixcloutier.com/x86/MAXSD.html |
This comment has been minimized.
This comment has been minimized.
|
@pornel No,
i.e. The definition of |
ranma42
reviewed
Mar 28, 2017
| input | ||
| } | ||
| } | ||
| ``` |
This comment has been minimized.
This comment has been minimized.
ranma42
Mar 28, 2017
Contributor
An alternative implementation which results in better code is:
pub fn clamp(input:f32, min: f32, max: f32) -> f32 {
let mut x = input;
if !(x < min) { x = min; }
if !(x > max) { x = max; }
x
}It conveniently preserves the source when it is NaN, as required in the edge cases listed below.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
ranma42
Mar 28, 2017
•
Contributor
... but it incorrectly returns NaN when max == NaN || min == NaN (EDIT: this is pseudocode... as crazy as it looks, the check for NaN would be max != max || min != min).
I am unsure if this is good or bad: while the infinity corresponding to no enforcement is straightforward, it is not obvious to me what is the desirable behaviour for NaN.
This comment has been minimized.
This comment has been minimized.
Xaeroxe
Mar 28, 2017
Author
Contributor
You know honestly that might be better behavior than what I proposed. It assumes a NaN is unintentional which they often are. If someone explicitly wants no bounds enforced they should provide infinity.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Should we mention the expected behaviour for |
This comment has been minimized.
This comment has been minimized.
|
@ranma42 I think so yes. max and min may not be constant values so it's not that far out to say a user could accidentally provide a max < min |
This comment has been minimized.
This comment has been minimized.
|
So I think the behavior for max < min should be explicitly defined but I don't really know what that behavior should be. My first thought is to just add a line at the top of the function: assert!(max >= min);I worry about |
This comment has been minimized.
This comment has been minimized.
bluetech
commented
Mar 28, 2017
|
If If they are not statically known, and the function is marked inline, then if it is called inside of a loop and So IMO panic is the safe way to go. |
This comment has been minimized.
This comment has been minimized.
|
@Xaeroxe that assert would also forbid passing |
This comment has been minimized.
This comment has been minimized.
|
Thanks @bluetech ! I learned some cool stuff about the optimizer today. |
This comment has been minimized.
This comment has been minimized.
|
Personally I prefer panic, as more harsh consequences for invalid input are more likely to make the programmer make sure to account for the possibility of invalid input. Of course if anyone has counter points to that I'd love to hear them. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@kennytm Good catch. How about this? pub fn clamp(input:f32, min: f32, max: f32) -> f32 {
assert!(max >= min);
let mut x = input;
if x < min { x = min; }
if x > max { x = max; }
x
} |
This comment has been minimized.
This comment has been minimized.
|
@kennytm sorry, you're right, I was misled by the documentation mentioning the first/second operand in Intel syntax vs the playground showing the GNU syntax. My bad. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Changes made, requesting feedback. Good work, I think we're getting closer to a final. |
This comment has been minimized.
This comment has been minimized.
|
For other languages/libraries having
Behaviors of different algorithms
|
aturon
assigned
scottmcm
Jul 25, 2017
This comment has been minimized.
This comment has been minimized.
|
@Xaeroxe Are you still in favour of the "on all the ranges" option? Are you planning to update the RFC to propose that instead? Is there anything with which you'd like help? Some potentially-interesting context: rust-lang/rust#25663 added default |
This comment has been minimized.
This comment has been minimized.
|
I apologize for my extended absence on this RFC. Now that I've come back to this later and re-evaluated it I think there's a trade-off to option 1 that we have to decide if it is worth it or not. Option 1 is more complicated to implement and use, but in exchange for that we get the ability to operate on more range types. So the question is, is that necessary? Will anyone ever have a need for a range that isn't fully closed? I think I'm going to investigate other languages and the usage of their clamp functions in order to get an answer to that question. |
This comment has been minimized.
This comment has been minimized.
|
I did some research on how other languages implement and use clamp, and for the most part they operate exclusively on fully inclusive ranges. The one exception is a library from npm with less than 10 downloads. So I don't feel the effort required to produce option 1 is really necessary and I'm leaving this RFC as is. I consider this RFC ready to merge. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Yep - I agree that the flexibility of an impl on ranges isn't needed in practice. |
scottmcm
reviewed
Aug 9, 2017
|
Yes, I agree this is ready to advance. |
|
|
||
| Likely locations would be on the Ord trait, and a special version implemented for f32 and f64. | ||
| The f32 and f64 versions could live either in std::cmp or in the primitive types themselves. There are good arguments for either | ||
| location. |
This comment has been minimized.
This comment has been minimized.
scottmcm
Aug 9, 2017
Member
Should probably update this now that the RFC has made a decision on where.
|
|
||
| Alternatives were explored at https://internals.rust-lang.org/t/clamp-function-for-primitive-types/4999 | ||
|
|
||
| Additionally there is the option of placing clamp in std::cmp in order to avoid backwards compatibility problems. This is however semantically undesirable, as `1.clamp(2, 3);` is more readable than `clamp(1, 2, 3);` |
This comment has been minimized.
This comment has been minimized.
scottmcm
Aug 9, 2017
Member
Adding min and max as methods on Ord caused a number of regressions (rust-lang/rust#42496 (comment)), and I'm unsure what their fate will be. Do we expect clamp to hit similar things?
This comment has been minimized.
This comment has been minimized.
Xaeroxe
Aug 9, 2017
Author
Contributor
@scottmcm Possibly, although to a much lesser magnitude I think. I guess the only way to know is to try it and see.
| # Unresolved questions | ||
| [unresolved]: #unresolved-questions | ||
|
|
||
| Is the proposed handling for NAN inputs ideal? |
This comment has been minimized.
This comment has been minimized.
scottmcm
Aug 9, 2017
Member
I think this was actually resolved (some of the inputs: https://internals.rust-lang.org/t/clamp-function-for-primitive-types/4999/19 #1961 (comment) #1961 (comment)); it might be worth a note in the RFC explaining why the specified NaN behaviour was chosen.
This comment has been minimized.
This comment has been minimized.
|
The shepherds for this RFC have signaled readiness for full team review, so here we go! @rfcbot fcp merge |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Aug 9, 2017
•
|
Team member @aturon has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
rfcbot
added
the
proposed-final-comment-period
label
Aug 9, 2017
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Aug 26, 2017
|
|
rfcbot
added
final-comment-period
and removed
proposed-final-comment-period
labels
Aug 26, 2017
This was referenced Aug 26, 2017
This comment has been minimized.
This comment has been minimized.
|
Tracking issue: rust-lang/rust#44095 |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Sep 5, 2017
|
The final comment period is now complete. |
This comment has been minimized.
This comment has been minimized.
|
Implementation PR is ready, it can be merged as soon as this is merged I believe. |
aturon
merged commit 9fad211
into
rust-lang:master
Sep 6, 2017
This comment has been minimized.
This comment has been minimized.
|
Merged! Thanks, @Xaeroxe, for sticking this one out. |
Mark-Simulacrum
added a commit
to Mark-Simulacrum/rust
that referenced
this pull request
Sep 6, 2017
Mark-Simulacrum
added a commit
to Mark-Simulacrum/rust
that referenced
this pull request
Sep 7, 2017
Xaeroxe
deleted the
Xaeroxe:Xaeroxe-clamp-rfc
branch
Sep 8, 2017
This comment has been minimized.
This comment has been minimized.
|
Clamp was implemented then shortly reverted due to concerns about breaking downstream code. It's unlikely this RFC will be implemented. See this: rust-lang/rust#44095 |
Xaeroxe commentedMar 27, 2017
•
edited
RFC for feature discussed at https://internals.rust-lang.org/t/clamp-function-for-primitive-types/4999
Tracking issue: rust-lang/rust#44095
Implementation PR: rust-lang/rust#44097