[HN Gopher] Math.min(Math.max(num, min), max)
       ___________________________________________________________________
        
       Math.min(Math.max(num, min), max)
        
       Author : tosh
       Score  : 177 points
       Date   : 2020-08-20 13:33 UTC (9 hours ago)
        
 (HTM) web link (twitter.com)
 (TXT) w3m dump (twitter.com)
        
       | tosh wrote:
       | in k:                 xs: 1 2 3 4 5       max: 4       min: 2
       | max& min| xs       2 2 3 4 4
       | 
       | works for scalar, vector, matrix
        
         | uryga wrote:
         | K made me realize that if you use the numbers 0 and 1 for
         | booleans, `min()` is `&&` and `max()` is `||`. love that!
        
           | tosh wrote:
           | same here, illuminating a-ha moment!
        
         | Jtsummers wrote:
         | After some of the recent J posts I spent a bit of time
         | refreshing myself on it:                 max <. min >. nums
         | 
         | or equivalently:                 min >. max <. nums
        
       | nibbapower69 wrote:
       | The author is one condescending shitcoder lmao
        
       | sanbor wrote:
       | Kotlin implementation [1]:                   public fun
       | Int.coerceIn(minimumValue: Int, maximumValue: Int): Int {
       | if (minimumValue > maximumValue) throw
       | IllegalArgumentException("Cannot coerce value to an empty range:
       | maximum $maximumValue is less than minimum $minimumValue.")
       | if (this < minimumValue) return minimumValue             if (this
       | > maximumValue) return maximumValue             return this
       | }
       | 
       | [1]
       | https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/c...
        
       | STRML wrote:
       | The basic function is simply defined as:                 function
       | clamp(num, min, max) {         return Math.max(min, Math.min(num,
       | max));       }
       | 
       | That is, if you don't try to do anything fancy and make any
       | parameters optional. Lodash does and it makes the implementation
       | much more complex.                 _.clamp(input: number, lower?:
       | number, upper: number): number;
       | 
       | https://github.com/lodash/lodash/blob/ddfd9b11a0126db2302cb7...
        
         | [deleted]
        
         | bonoboTP wrote:
         | Or to make sure it's crystal clear what's going on:
         | function clamp(num, min, max) {           if (num > max)
         | return max;           if (num < min)             return min;
         | return num;         }
        
           | 52-6F-62 wrote:
           | Speaking only to JS is there any reason to write it any other
           | way outside of being clever or as a lambda for singular use?
           | I definitely prefer this version. (Assuming any necessary
           | runtime checks are included for a given project)
        
             | chasd00 wrote:
             | those extra newline characters slow down the page load :)
        
               | tobyhinloopen wrote:
               | You're joking right
        
             | beirut_bootleg wrote:
             | There are many reasons to forego readability, especially
             | when writing a library: performance, compatibility,
             | requirements, interpreter/compiler optimizations or even
             | cyclomatic complexity.
             | 
             | In lodash's case it might even be all of the above,
             | although I can't speak for the intentions of the authors
             | since there are no comments to guide readers through the
             | process.
             | 
             | Note GP's link points to what looks like the v3 branch.
             | Check out the latest implementation of clamp, with a few
             | less if statements, and what looks like a NaN check using
             | strict equality if you want your mind blown. https://github
             | .com/lodash/lodash/blob/86a852fe763935bb64c125...
        
           | matthewmacleod wrote:
           | Seriously. This is about a million times better.
        
             | mywittyname wrote:
             | Absolutely. With the above implementation, I can see
             | exactly what's going on, with the others, I'm trying to
             | work out potential edge cases.
        
               | brandmeyer wrote:
               | I prefer the min/max-based definition for the same
               | reason: Its easier to work out the edge cases around NaN
               | and comparisons against NaN.
        
           | jaffathecake wrote:
           | Yep, I came to the same conclusion
           | https://twitter.com/jaffathecake/status/1296423819238944768
        
           | Akronymus wrote:
           | Now in C# with pattern matching:                   int
           | clamp(num, min, max)           {             return num
           | switch                    {                      _ when num >
           | max => max,                      _ when num < min => min,
           | _ => num                    };           }
           | 
           | Or as a lambda:                   Func<int, int, int, int>
           | clamp = (num, min, max) => num switch {_ when num > max =>
           | max, _ when num < min => min,_ => num};
        
       | pachico wrote:
       | I would make it more confusing to read by using the 100 and 0
       | percentiles.
        
       | donquichotte wrote:
       | Luckily, this is a solved problem for go.                 func
       | helper(a float64, c chan float64){
       | time.Sleep(time.Duration(a) * time.Second)           c <- a
       | }              func clamp(a float64, min float64, max float64)
       | float64 {           c := make(chan  float64, 3)           go
       | helper(a, c)           go helper(min, c)           go helper(max,
       | c)           _, out, _ := <-c, <-c, <-c           return out
       | }
        
         | resist_futility wrote:
         | Isn't this a race condition?
         | https://play.golang.org/p/3Im8FuhyR_S
        
         | apta wrote:
         | aka sleep sort
        
         | ccmcarey wrote:
         | That will give you the median value. What OP wants is the value
         | `a` clamped within `min` and `max`.
        
           | donquichotte wrote:
           | Can you give an example where median([a, min, max]) is not
           | equal to clamp(a, min, max), given max >= min?
        
             | naikrovek wrote:
             | If a < min or a > max, if I'm reading it right.
        
               | mc10 wrote:
               | You can prove that this algorithm works for both of those
               | cases. Assuming min <= max:
               | 
               | If a < min, then the sorted array is [a, min, max]. The
               | median is min, which is a clamped to min.
               | 
               | If a > max, then the sorted array is [min, max, a]. The
               | median is max, which is a clamped to max.
        
         | tl wrote:
         | Passing math.NaN() crashes in a playground.
        
       | bigganibba69 wrote:
       | Damn, this dude is one condescending shitcoder lmao
        
       | patresh wrote:
       | I find that the fact that the functions min and max have the same
       | name as the variables min and max increases cognitive load which
       | makes it harder to think about it.
       | 
       | I find the following easier to read :
       | Math.min(Math.max(num, lower_bound), upper_bound)
        
         | amichal wrote:
         | Assuming we know (lower_bound <= upper_bound) I'd write:
         | 
         | Math.max(lower_bound, Math.min(num, upper_bound))
         | 
         | Since i read right to left.
        
           | biddlesby wrote:
           | It's amazing what a difference it makes when you just good
           | names and formatting
        
         | loopz wrote:
         | I find the extra words wordy.
        
         | boxfire wrote:
         | I think this was posted purely for the limerick quality.
        
           | jkaptur wrote:
           | In a file of utility hacks
           | 
           | near get(), post(), and ajax()
           | 
           | // we alias at import
           | 
           | // to keep all the code short
           | 
           | min(max(number, min), max)
        
           | gmfawcett wrote:
           | Haiku is nice, too:                   Cryptic sorcerer!
           | "Math.min(Math.max(v, min), max);"         his incantation.
        
           | hanoz wrote:
           | There was a young coder whose hacks       His manager often
           | claimed lacked       The requisite clarity       For to clamp
           | vars would he:       Math.min(Math.max(number, min), max);
        
             | drbacon wrote:
             | @jaffathecake had a problem of truncation         and
             | posted to Twitter his calculation.         The gist of his
             | attack         was min( max( num, min ), max)         yet
             | refused to add any annotation.
        
             | [deleted]
        
         | parliament32 wrote:
         | I prefer "ceiling" and "floor", but yes, agreed.
        
           | KONAir wrote:
           | I use ceil/floor (and make people use whenever I can) if
           | something is going to happen when something hits the ceiling
           | or drops to the floor. And avoid if it is just for clamping.
        
           | gugagore wrote:
           | Those also happen to be fairly common names for operations
           | (rounding up or down to nearest integer).
        
         | jacobolus wrote:
         | It's usually nicer to make a helper function:
         | const clamp = (x, low, high) =>         Math.max(low,
         | Math.min(x, high));
         | 
         | Then it can be easily used later via a descriptive name. e.g.
         | color_component = clamp(color_component, 0, 255);
        
         | Someone wrote:
         | Easy to remember, but may take some time to grasp:
         | Arrays.sort( {lower_bound, num, upper_bound} )[1];
         | 
         | Next challenge: teach the optimizer to make that almost as fast
         | as the min/max way ;-)
         | 
         | (You can't reduce it to the min/max call because it also works
         | if you accidentally pass a lower bound that's larger than the
         | upper bound. Worst-case, the above takes 3 comparisons, unless
         | at least two of the inputs are constants)
        
           | mabbo wrote:
           | You know what's great about that? The order of the arguments
           | doesn't matter. So all the debate about "should it be num,
           | min, max or min, num, max"- your solution does not care. Put
           | them in any order you like!
           | 
           | You've redefined the problem from clamping a given value into
           | picking the middle value from 3. This is a lovely way to re-
           | interpret it.
        
             | thaumasiotes wrote:
             | Well, the order of the arguments does matter. Sorting three
             | values requires 2.67 comparisons where clamping a value
             | between two other values requires exactly 2. There are
             | plenty of contexts where cleverly avoiding a problem by
             | doing 33% more work isn't viewed as desirable.
        
           | chrisseaton wrote:
           | > Next challenge: teach the optimizer to make that almost as
           | fast as the min/max way ;-)
           | 
           | I did _exactly_ this for my PhD!
           | 
           | https://chrisseaton.com/phd/
        
             | ford_o wrote:
             | Wait, how is that not trivial?
        
               | saagarjha wrote:
               | It's not just a hardcoded optimization for that
               | construct.
        
               | chrisseaton wrote:
               | > how is that not trivial?
               | 
               | It could be trivial to implement an optimisation which
               | does this for that exact code. But what are you going to
               | do? Hand-code an optimisation for every similar thing
               | people could write? I implemented a general solution.
               | 
               | So it also works through metaprogramming:
               | [1, 2, 3].send(:sort).send(:[], 1)
               | 
               | Through user-defined sorting order:                   [1,
               | 2, 3].sort_by { |a, b| b <=> a }[1]
               | 
               | When nested:                   [[1, 2].sort[1],
               | 3].sort[0]
               | 
               | And so on.
               | 
               | Note that it also needs to be transparent to debuggers
               | and profilers, it needs to handle multiple method
               | redefinitions (for example what happens if someone
               | redefines the sorting order for integers).
               | 
               | It's not a pattern-matching optimization - it's partial
               | evaluation enabled by a new kind of polymorphic inline
               | cache.
        
               | azinman2 wrote:
               | > But what are you going to do? Hand-code an optimisation
               | for every similar thing people could write?
               | 
               | While I appreciate that you solved the general problem, I
               | wonder if there are legs here. Specifically, could one
               | mine GitHub to find automatically common patterns that
               | one could write specific optimizers for, or at minimum,
               | leverage that to learn what semi-general cases are worth
               | optimizing? To my knowledge optimizing compilers already
               | do have effectively handlers for common operations, but I
               | don't know if anyone has leveraged "big data" to help
               | guide this.
               | 
               | IIRC, various tweaks and optimizations in Java were
               | guided by Sun analyzing their own code based. GitHub is
               | just so much bigger, and polyglot.
        
             | Lerc wrote:
             | I'm glad things like this are being worked on. I have been
             | writing a set of implementations of the nBody benchmark in
             | JavaScript using various forms of abstractions. In an ideal
             | world they should all take the same speed since they
             | perform the same fundamental task and produce the same
             | result. They just represent different scales of
             | optimization effort.
             | 
             | It's interesting seeing the difference between vectors in
             | arrays vs objects and if you do immutable versions. The
             | trickiest to optimize form is using a micro vector library
             | which uses closures and array map().                   var
             | vop = op => ((a, b) => (a.map((v, i) => op(v, b[i]))));
             | var vdiff = vop((a, b) => a - b);         var vequals = (a,
             | b) => {  return (vdiff(a, b).reduce((c, d) => c +
             | Math.abs(d), 0)) === 0;};         var vadd = vop((a, b) =>
             | a + b);         var vdot = (a, b) => a.reduce((ac, av, i)
             | => ac += av * b[i], 0);         var vlength = a =>
             | Math.sqrt(vdot(a, a));         var vscale = (a, b) =>
             | a.map(v => v * b);         var vdistance = (a, b) =>
             | vlength(vdiff(a, b));
             | 
             | Currently on my lowly atom laptop and FireFox. The version
             | using mutable objects is ten times faster than the mapping
             | immutable array version. I live in hope that one day there
             | will be an optimizer that turns                   var
             | transpose = matrix => (matrix.reduce(($, row) =>
             | row.map((_, i) => [...($[i] || []), row[i]]), []));
             | var multiply = (a, b, ...rest) => (!b) ? a :
             | multiply(a.map((p => transpose(b).map(q => vdot(p, q)))),
             | ...rest);
             | 
             | Into a CPU optimum (or dare I suggest GPU) version of a
             | matrix multiply.
        
             | TimTheTinker wrote:
             | For those reading, this work went into TruffleRuby, which
             | implements Ruby on top of Truffle/GraalVM.
        
             | [deleted]
        
           | gowld wrote:
           | If you are going to call a library function, just call
           | median().
        
           | fizixer wrote:
           | I think some of us (most? all?) develop a tendency to act/be
           | clever instead of act/be clear. When I resist that
           | temptation, I ask for something like:
           | clamped(num, range=[ lower_bound, upper_bound ]);
           | 
           | and then have no interest in how this actually gets
           | implemented.
        
           | xxs wrote:
           | this doesn't work for NaN, e.g {0d, Double.NaN, 1d} returns
           | 1d.
        
             | im3w1l wrote:
             | So I tried a thing in python3.                   >>>
             | max(float('nan'), 0)         nan         >>> max(0,
             | float('nan'))         0
             | 
             | Numpy works though                   >>>
             | np.maximum(float('nan'), 0)         nan         >>>
             | np.maximum(0, float('nan'))         nan
             | 
             | Edit: Fixed numpy example.
        
               | dr_zoidberg wrote:
               | And for this particular use case, numpy has .clip(), that
               | takes:                   np.clip(ndarray, lower_bound,
               | upper_bound)
               | 
               | And handles NaNs correctly.
        
               | rrobukef wrote:
               | The functions minNum and maxNum ([IEEE 754-2008, 5.3.1,
               | p19]) take two arguments and return the min and max,
               | respectively. They have the special, distinguished
               | property that "if exactly one argument is NaN, they
               | return the other. If both are NaN they return NaN."
               | 
               | Source: http://tom7.org/nand/nand.pdf
               | 
               | Edit: As of 2019, the formerly required minNum, maxNum,
               | minNumMag, and maxNumMag in IEEE 754-2008 are now deleted
               | due to their non-associativity.
               | [https://en.wikipedia.org/wiki/IEEE_754#2019]
        
             | taberiand wrote:
             | Having a NaN at that point feels like a bug anyway, the
             | solution is probably to check the arguments and throw an
             | exception if NaN is provided (or use an input type that
             | doesn't allow invalid values)
        
             | jrochkind1 wrote:
             | Does NaN have an "order" in the set of reals or integers or
             | whatever? I would have no idea what to expect from
             | `min(NaN, x)` or max same. But is it specified by an IEEE
             | standard or something?
        
               | loeg wrote:
               | No, the only valid result for NaN is NaN. Saturating
               | range bounds are for real numbers.
        
               | xxs wrote:
               | Both min and max should return NaN, if any of their
               | parameters is NaN. Sorting can be defined where to place
               | the NaNs (head/tail) but it's largely irrelevant in this
               | case as simply the substitution won't be permitted by any
               | compiler.
               | 
               | NaN is part of IEEE754 but of course it's not a 'real'
               | number (integer numbers don't have NaNs)
               | 
               | Edit: you can consider NaN (and to a degree both
               | infinities) as an exception, once it occurs - it has to
               | be propagated. Any operation involving NaN should be
               | returning NaN, any operation comparing NaN to anything
               | has to return 'false'. That includes "if (NaN == NaN)".
               | boolean isNaN(double d) is effectively "return d != d;"
        
               | noctune wrote:
               | > Both min and max should return NaN, if any of their
               | parameters is NaN.
               | 
               | That's not a given, though. IEEE 754-2008 defined min and
               | max as returning the non-NaN parameter. They have been
               | removed in IEEE 754-2019 though.
        
               | xxs wrote:
               | The reference to IEEE 754 is made later on, mostly to
               | answer the question posted. I meant regular functions in
               | C alike languages - Math.min/max - java/javascript,
               | fmin/fmax - C++. They do the "right" thing to propagate
               | the NaN
        
               | thaumasiotes wrote:
               | No, it breaks the ordering requirements. NaN compares
               | greater than and less than every number.
               | 
               | You can say that a call to max should return NaN if any
               | argument is NaN, but you can't say the same about
               | sorting. (For one thing... sorting an array doesn't
               | return a scalar value.) Sorting is done with comparisons,
               | and what happens if a NaN gets into the list of values
               | will depend on which specific comparisons happen to be
               | done.
        
               | monktastic1 wrote:
               | > Does NaN have an "order" in the set of reals or
               | integers or whatever?
               | 
               | By _definition_ , something that is _not_ a number (real,
               | integer, etc.) cannot be compared to something that _is_
               | a number.
        
               | shakow wrote:
               | It depends on what space you're working on (e.g. the
               | https://en.wikipedia.org/wiki/Extended_real_number_line
               | define an order on the real field union {-[?], +[?]}).
        
         | [deleted]
        
         | Dylan16807 wrote:
         | That doesn't really help. "Max" to enforce a "lower bound" is
         | briefly halting.
        
           | airstrike wrote:
           | Maybe add an alias to Max called AtLeast and one for Min
           | called AtMost to be used in these situations ;)
        
           | hyh1048576 wrote:
           | I agree.
           | 
           | But "min(-, constant_x)" should be thought of as "at most
           | constant_x" and similarly for max. Maybe there's a way to
           | make it more expressive.
        
             | ncallaway wrote:
             | I think your "at most" language is pretty expressive. You
             | could do that as an alias for `min` and `max`
             | 
             | I think `at_most(at_least(num, lower_bound), upper_bound)`
             | is much easier to understand instantly than
             | `min(max(...))`.
             | 
             | I'm tempted to make these aliases myself in some of my
             | development actually. I find a pretty big conceptual
             | difference between "I want to find the minimum point in
             | this data", and "I want to restrict the range of this
             | number" that giving them different names will probably help
             | the readability of my code.
             | 
             | (Of course, for `min(max(...))` I usually write a `clamp()`
             | function to hide that for me, but someones I want to only
             | clamp in one direction)
        
               | dmurray wrote:
               | > (Of course, for `min(max(...))` I usually write a
               | `clamp()` function to hide that for me, but someones I
               | want to only clamp in one direction)
               | 
               | You could make clamp work in only one direction too.
               | clamp(number, None, upper_bound) or the idiomatic
               | equivalent in your language of choice seems pretty
               | readable.
        
           | baddox wrote:
           | I think the reason it's weird is that we might intuitively
           | think of the "enforce a lower bound" function as taking two
           | named arguments (lowerBound and inputValue) and _the order of
           | those two arguments mattering_.
           | 
           | But of course, it turns out that the order of the arguments
           | doesn't matter: applying a lowerBound of 5 to an inputValue
           | of 100 turns out to be the exact same thing as applying a
           | lowerBound of 100 to an inputValue of 5.
           | 
           | We know that the order of arguments doesn't matter for the
           | Math.max function, so I think that's where the moment of
           | incredulity comes from.
        
           | Symbiote wrote:
           | I would prefer it if the methods in java.lang.Math had been
           | called "larger" and "smaller", instead of "min" and "max".
           | 
           | I sometimes mix up "min" as "take the minimum" rather than
           | "take the larger given this minimum".
        
             | t0astbread wrote:
             | But "min" does take the minimum: `Math.min(1,3) == 1`
        
       | w-m wrote:
       | I personally would find this much more straightforward:
       | val - (sign(val - max) + 1) / 2 * (val - max) + (sign(min - val)
       | + 1) / 2 * (min - val)
        
       | O_H_E wrote:
       | I always though pipe operators are under-rated and under-used.
       | 
       | In elixer that could be
        
       | [deleted]
        
       | moralestapia wrote:
       | >[...] it takes me ages to convince myself this implementation is
       | correct.
       | 
       | Is that supposed to be difficult?
       | 
       | Not trying to be snarky, I'm honestly surprised, do people
       | actually struggle with this? Googlers in particular?
        
         | im3w1l wrote:
         | Took me a few minutes first time I saw it but it's now firmly
         | in my mental library of idioms.
        
         | young_unixer wrote:
         | x2
         | 
         | While doing competitive programming I learned that I'm really
         | stupid, but even as stupid as I am, I still understand that
         | expression easily.
        
       | throwaway6734 wrote:
       | This is something that upon seeing I would sketch out. It's way
       | easier for me to understand visually
        
       | antoineMoPa wrote:
       | When making glsl shaders, there's clamp().
        
       | polote wrote:
       | > it takes me ages to convince myself this implementation is
       | correct.
       | 
       | > Googler
       | 
       | Then this is another proof that google doesnt only hire on
       | mathematical puzzle tricks
        
       | da39a3ee wrote:
       | https://twitter.com/ben_a_adams/status/1296463312780251136
        
       | [deleted]
        
       | spapas82 wrote:
       | Here's clamp in idiomatic Elixir (using multi-clause functions
       | and guards):                 def clamp(min, _max, n) when n<min,
       | do: min       def clamp(_min, max, n) when n>max, do: max
       | def clamp(_min, _max, n), do: n
        
         | nesarkvechnep wrote:
         | defmodule Math do         def clamp(num, _min, max) when num >
         | max, do: max         def clamp(num, min, _max) when num < min,
         | do: min         def clamp(num, _min, _max), do: num       end
        
         | im3w1l wrote:
         | Following xxs example of looking at NaN behavior, with this
         | code, a bound (say lower) of NaN means that bound is disabled.
         | Which may or may not be what you want.
        
         | grantjpowell wrote:
         | An Elixir convention I've seen is to put the thing you're
         | operating on first, so that you can compose functions using the
         | `|>` operator, which places the previous expression as the
         | first argument of the function to the right.
         | 
         | Maybe something like this?                 defmodule Compare do
         | def clamp(number, minimum, maximum) do           number
         | |> max(minimum)           |> min(maximum)         end       end
         | import Compare              clamp(5, 1, 10) # 5       clamp(1,
         | 5, 10) # 5       clamp(10, 1, 5) # 5
         | some_number       |> clamp(min, max)
         | 
         | As a side note, I think the Elixir |> operator is a stroke of
         | genius that other languages should take a look at. Making the
         | pipe operator append the _first_ argument has the following
         | benefits
         | 
         | 1.) It makes the most "important" argument of the function the
         | first thing you read in function signatures
         | 
         | 2.) If you need to add more arguments to a function signature
         | later, they tend to be less important the original args, so
         | they tend to make sense at the end
         | 
         | 3.) It creates a convention for all libraries to follow so they
         | can leverage the pipe operator. Its really jarring when the
         | thing you want to put in a pipeline isn't the first argument
         | (looking at you `Regex`[0] which puts the regular expression as
         | the first arg and not the string)
         | 
         | [0] https://hexdocs.pm/elixir/Regex.html#replace/4
        
           | spapas82 wrote:
           | Yes you are right! Puting the number in the first parameter
           | is even more idiomatic Elixir.
        
       | css wrote:
       | Very coincidental I see this post almost immediately after
       | writing the same code:                   new_poll_rate = \
       | min(                 max(                     1 /
       | messages_per_second,
       | constants.FASTEST_POLL_RATE                 ),
       | constants.SLOWEST_POLL_RATE             )
       | 
       | I agree with the sentiment, I had to re-read this several times
       | to make sure I got it right.
        
         | bovine3dom wrote:
         | If fastest poll rate > slowest poll rate, I think you've got
         | them the wrong way around (or is that the joke?).
        
           | css wrote:
           | FASTEST_POLL_RATE is 1000hz, SLOWEST_POLL_RATE is 10hz. Thus,
           | FASTEST_POLL_RATE is a smaller number on a per-second basis.
        
             | bovine3dom wrote:
             | Ah, I see - for clarity I'd rename them FASTEST_POLL_RATE
             | -> SHORTEST_POLL_PERIOD or store them in Hz rather than
             | seconds, so everything was 1/ in that little snippet.
             | Thanks for clearing up my confusion :)
        
               | css wrote:
               | Yeah that's a very good call, I will probably rename
               | these. Thanks for the critique!
        
           | stkdump wrote:
           | It seems like they are just have bad names, as their unit
           | probably is seconds not 1/seconds.
        
       | lalaithion wrote:
       | This is a great example of how haskell makes these things obvious
       | ;)                   clamped = num `max` min' `min` max'
        
         | graham_paul wrote:
         | no idea what's going on there, I know some of those variables
         | are actually functions but the whole thing is unreadable unless
         | you have experience in haskell imo
        
           | masklinn wrote:
           | The backticks turn regular functions into left-associative
           | operators of the highest priority (by default).
           | 
           | So                   a `foo` b `bar` c
           | 
           | is                   (bar (foo a b) c)
        
             | O_H_E wrote:
             | Oohh nice. So kinda like a pipe.
             | 
             | I really think we deserve more syntax that allowed
             | something like this, because otherwise one needs to read
             | `(bar (foo a b) c)` inside-out.
        
               | masklinn wrote:
               | > Oohh nice. So kinda like a pipe.
               | 
               | I never thought of it that way, but it is true that it
               | can be used as a pipe.
               | 
               | Fundamentally it's just the dual of (): Haskell lets you
               | use operators as infix functions by wrapping them in ()
               | so                   (+) 1 2
               | 
               | is the same as                   1 + 2
               | 
               | and conversely lets you use prefix (binary) functions as
               | operators by wrapping them in backticks.
               | 
               | You can even combine those through _sections_ , which
               | serve to partially apply infix operators:
               | https://wiki.haskell.org/Section_of_an_infix_operator
        
       | jchw wrote:
       | I always used to get confused by the function names "min" and
       | "max" because they return the minimum and maximum, but typically
       | when you use them you are thinking in terms of _applying_ a
       | minimum or maximum bound. Having a dedicated clamp function
       | significantly helps although imo there is not a single correct
       | order for the parameters to go in.
        
         | z3ncyberpunk wrote:
         | More often than not no.. I am trying to find the min/max value
         | in a set. Bit a bound
        
       | 9nGQluzmnq3M wrote:
       | Not as elegant, but way more readable:
       | 
       | if num > max: num = max
       | 
       | if num < min: num = min
        
       | geophile wrote:
       | OPs implementation requires 2 comparisons. I think this C
       | expression is clearer, and will sometimes use 1 comparison:
       | num < min ? min :         num > max ? max :         num
        
         | [deleted]
        
         | xondono wrote:
         | To me the danger of this kind of one liners is that if you use
         | it 2 times somewhere, someone, at some point, will do the lazy
         | refactor of "let's put it into a function".                 int
         | clamp (int value, int min, int max)
         | 
         | And that one liner inside. And now you have a double evaluation
         | bomb waiting to go out on your codebase.
        
           | geophile wrote:
           | Why? You wrote a function, not a macro.
        
             | xondono wrote:
             | Accidentally overwrote the middle step:
             | 
             | Junior developer puts the one liner on google, finds the
             | typical macro definition and does the change.
        
             | adrianmonk wrote:
             | Maybe they mean that you might have been unintentionally
             | relying on double evaluation, and then you take it away,
             | and something breaks. Because it worked for the wrong
             | reasons.
        
         | jacobolus wrote:
         | Branching is the expensive part, not comparisons.
        
         | edflsafoiewq wrote:
         | Incidentally, gcc and clang both compile this to two cmovs for
         | me.
        
           | geophile wrote:
           | I don't speak x86. Do you mean that these compilers are
           | evaluating both conditions, all the time? At what
           | optimization level?
        
             | edflsafoiewq wrote:
             | Yes, you can check on godbolt. At -O{1,2,3}. At -Os, clang
             | still generates cmovs, but gcc generates a jump.
        
             | benchaney wrote:
             | x86 has instructions that execute conditionally. Most
             | conditionals in higher level languages get compiled to
             | conditional jumps, but using conditional operations this
             | isn't necessary. The same code path is taken in all cases,
             | and the instructions effect is what is conditional.
             | 
             | In the case of cmov, it is either a nop or a mov dependent
             | on the state of the conditional flags. Using this construct
             | instead of a regular mov guarded by conditional jumps has
             | better performance in some cases.
             | 
             | On my machine gcc is outputting a combination of
             | conditional jumps and conditional movs at all optimization
             | levels
        
       | [deleted]
        
       | wonderlg wrote:
       | There's an obvious solution to this:
       | require('clamp')(num, min, max)
       | 
       | 1 million monthly downloads
        
       | aldanor wrote:
       | Once you've seen this a few dozen times, you instantly parse it
       | as "clamp(num, min, max)"
        
         | hombre_fatal wrote:
         | Did a bunch of coding bootcamps just begin session or
         | something? I don't understand the comments here treating it
         | like it's so hard to write and verify that it needs a
         | completely different, slower, less direct, cutesy
         | implementation.
        
           | aldanor wrote:
           | I don't understand the explosion of comments here either. I
           | was just saying for myself, it's a trivial construct and very
           | commonly encountered.
        
       | tzs wrote:
       | 68K assembly, no branches:                 # d0 = num, d1 = low,
       | d2 = high, d3 = scratch       sub.l   d1, d0       subx.l  d3, d3
       | or.l    d3, d0       addx.l  d1, d0  # d0 = max(num,low)
       | sub.l   d2, d0       subx.l  d3, d3       and.l   d3, d0
       | add.l   d2, d0  # d0 = min(max(num,low),high)
       | 
       | Adapted from "Superoptimizer -- A Look at the Smallest Program"
       | by Henry Massalin [1].
       | 
       | [1]
       | https://web.stanford.edu/class/cs343/resources/superoptimize...
        
         | saagarjha wrote:
         | Note that with modern branch predictors such optimizations may
         | not actually be beneficial-ARM got rid of condition codes on
         | all its instructions in the 64-bit version. (Plus, I assume
         | they ran out of space to encode them.)
        
           | brandmeyer wrote:
           | IEEE754 has defined min and max for quite some time. Nowadays
           | almost all FPUs have min and max instructions built-in.
        
         | kazinator wrote:
         | Isn't .l implied if it is omitted?
         | 
         | If I just say "sub d1, d2", of course I mean the whole register
         | width, and not just 16 or 8 bits of it.
        
       | O_H_E wrote:
       | I think the main hurdle is that we need to read this inside-out,
       | which is something we face regularly trying to read nested
       | function calls. I seriously believe pipe-like operators solve
       | this problem cleanly.
       | 
       | In Elixir which does have the pipe `|>` this could be.
       | number = number |> max(lower_bound) |> min(upper_bound)
       | 
       | Which IMO is quite elegant.
        
         | O_H_E wrote:
         | Note: I don't really know Elixir, all I know is that this works
         | in repl.it
        
           | jakeva wrote:
           | I use Elixir day to day, and I agree, piping is one of the
           | nicest things about it. And your code is the way I would have
           | written it.
        
       | parliament32 wrote:
       | Python also doesn't have a built-in clamp function (I think there
       | might be one in numpy), I usually use
       | sorted((floor, x, ceiling))[1]
       | 
       | Throw it in a function with some asserts if you're worried about
       | x not being a number and giving you weird results.
        
         | doukdouk wrote:
         | There is indeed one in numpy :
         | https://numpy.org/doc/stable/reference/generated/numpy.clip....
        
         | dagurp wrote:
         | You could use statistics.median (in python 3.6+)
        
       | [deleted]
        
       | ridiculous_fish wrote:
       | Most of the ternary implementations here are less correct than
       | the original. You generally want min/max to satisfy the
       | following:
       | 
       | 1. min/max of NaN and anything is NaN
       | 
       | 2. negative 0 is strictly smaller than positive 0
       | 
       | it's a fun exercise to implement clamp under these constraints.
        
       | ebg13 wrote:
       | > _it takes me ages to convince myself this implementation is
       | correct_
       | 
       | It would take them less time to convince themself if they
       | switched min and num to put the values in proper semantic order:
       | min < num < max.
       | 
       | Math.min(Math.max(min, num), max)
        
         | jaffathecake wrote:
         | I don't think so. Whereas an if statement is easy to read
         | https://twitter.com/jaffathecake/status/1296423819238944768
        
         | bhaak wrote:
         | That still leaves the order of Math.min and Math.max undecided
         | and will probably not help much if you get easily confused by
         | the visuals of this code.
         | 
         | I never thought about it but of course there must be code
         | tongue twisters (or more correctly, brain twisters).
         | 
         | Thinking about it, I would probably go with a less confusing
         | implementation. Terse code is hard to read and the compiler is
         | likely clever enough to choose the best implementation anyway.
        
       | rewq4321 wrote:
       | Stage 1 ECMAScript proposal to add Math.clamp (among others):
       | https://github.com/rwaldron/proposal-math-extensions
       | 
       | But it looks dead: https://github.com/rwaldron/proposal-math-
       | extensions/issues/...
       | 
       | As someone mentioned in the thread, nested ternary is easier to
       | interpret:
       | 
       | (a > max ? max : (a < min ? min : a))
        
         | macspoofing wrote:
         | To each his own but I disagree. Nested ternary's are hard to
         | read and understand, and modifying them (by future devs) is
         | tricky and error prone.
        
           | [deleted]
        
         | sarah180 wrote:
         | At the risk of starting a style debate, this may be easier to
         | read if you write it like an if-else:                   a > max
         | ? max :         a < min ? min :                   a
        
           | KMag wrote:
           | Unless, of course, your language "designer" has no idea why
           | every other language uses right-associative ternary
           | operators.
        
             | bangonkeyboard wrote:
             | This was a "fun" bug to track down in a PHP codebase.
        
         | jaffathecake wrote:
         | I don't find this a whole lot easier to read to be honest. It
         | seems like doing minification manually, when we have tools to
         | do that for us. An if statement seems a lot clearer, and
         | minifies well
         | https://twitter.com/jaffathecake/status/1296423819238944768
        
           | balfirevic wrote:
           | It's just a matter of what you're used to. Both of those are
           | equally clear to me.
        
       | Haga wrote:
       | inLimits(lowerBound, Val, upperBound)
        
       | ChrisLomont wrote:
       | I find the following much easier to read. It's certainly easier
       | to maintain, deal with, for successive programmers, and likely
       | has exactly the same compiled/interpreted operations for most
       | common languages. Optionally replace t1 and t2 by overwriting num
       | if needed.
       | 
       | Use full if else if you must.
       | 
       | If you're worried about speed, using built in min and max is not
       | a good idea. There are many, many tricks to remove branches for
       | certain datatypes, etc., as you need.
       | 
       | var t1 = num < min ? min : num; // clamp to min var t2 = max < t1
       | ? max : t1; // clamp to max return t2; // num clamped to
       | [min,max]
        
       | kazinator wrote:
       | This is the TXR Lisp interactive listener of TXR 242.       Quit
       | with :quit or Ctrl-D on an empty line. Ctrl-X ? for  cheatsheet.
       | 1> (clamp 1 10 -1)       1       2> (clamp 1 10 15)       10
       | 3> (clamp 1 10 5)       5
       | 
       | Added on August 13, 2015 by commit
       | f2e197dcd31d737bf23816107343f67e2bf6dd8e
        
       | throwawaw wrote:
       | Fun seeing that pretty much everyone else finds that idiom
       | confusing too. Half-serious, over breakfast:
       | (case [(> n min) (< n max)]           [true true] n
       | [true false] max           [false true] min)
       | 
       | (Side note: Clojure's `>` and `<` are kind of unreadable to begin
       | with. Turning `if (> n min)` into "if n is greater than min"
       | takes some work for me, still, after more than a year.)
        
         | kickopotomus wrote:
         | Alternatively, since min and max are part of clojure.core:
         | (-> n            (max vmin)            (min vmax))
        
         | white-flame wrote:
         | What about the [false false] case where min >= n >= max?
        
         | retzkek wrote:
         | However, prefix notation becomes much easier to read and reason
         | about when you have more than two things to compare:
         | (> 3 2 1 ...)
         | 
         | vs                   (3 > 2) && (2 > 1) && ...
        
       | beervirus wrote:
       | Is this a problem that comes up often? What kind of situation
       | would make you want to calculate this?
        
         | mr_toad wrote:
         | It's pretty common in data science or financial applications to
         | restrict the range of data in this way.
         | 
         | There are probably SQL devs reading this and wondering what the
         | fuss is about.
        
         | baddox wrote:
         | It's common in computer graphics and user interface
         | programming. You want to move your character around a 2D grid,
         | but you don't want either of its x and y coordinates to ever
         | move outside the bounds of the 2D grid. Or you want a web page
         | with an article section that is 50% of the browser width, but
         | never narrower than 300px and never wider than 800px.
        
       | gre wrote:
       | In Factor:                   : clamp ( x min max -- y ) [ max ]
       | dip min ; inline
       | 
       | https://docs.factorcode.org/content/word-clamp,math.order.ht...
        
       | mauricio wrote:
       | Always good to know the underlying implementation, but for any
       | Ruby readers check out clamp(). It's been available since 2.4.
       | 25.clamp(5, 10)       => 10            6.clamp(5, 10)       => 6
       | 1.clamp(5, 10)       => 5
        
         | madcaptenor wrote:
         | I find myself needing this most frequently in making graphics
         | in R. The scales package has squish() with the same behavior:
         | 
         | squish(25, c(5, 10)) => 10
         | 
         | squish(6, c(5, 10)) => 6
         | 
         | squish(1, c(5, 10)) => 5
         | 
         | If you don't provide the limits it defaults to c(0, 1). That's
         | because this function exists to map to a 0-to-1 range for
         | functions that then map the [0, 1] range to a color ramp.
        
         | chrisseaton wrote:
         | I often see [a, b, c].sort[1] which I think is very neat.
        
           | nitrogen wrote:
           | If you are writing code that is clamping a lot of numbers,
           | the GC churn from building a new array every time might be a
           | problem.
        
             | chrisseaton wrote:
             | Ideally the temporary array should optimise away.
        
           | rubber_duck wrote:
           | I'd say the only way this would be better than min/max
           | solution is that you can't accidentally flip it when writing
           | the code - but IMO both suck at expressing intent, clamp
           | reads unambiguously.
        
           | stickyricky wrote:
           | Yes but its too many operations for such a simple problem. No
           | need to overwork the system when two conditionals can solve
           | the problem.
        
       | ossopite wrote:
       | Excel has no clamp function but a neat alternative is
       | =MEDIAN(min, num, max)
        
       | OscarCunningham wrote:
       | Ideally I'd also want to throw an error when max < min.
        
       | rkagerer wrote:
       | Wrap it in a function, verify the implementation once, then
       | henceforth use clamp(x, min, max).
        
       | Const-me wrote:
       | In languages I use there's usually no need to write that code.
       | 
       | C++/17 has std::clamp() in <algorithm> header.
       | 
       | Modern C# has Math.Clamp() since .NET Core 2.0; too bad it's not
       | available in desktop edition of the runtime.
       | 
       | HLSL has clamp() intrinsic function, and a special version
       | saturate() to clamp into [ 0 .. +1 ] interval.
        
         | OkGoDoIt wrote:
         | I had no idea .NET has Clamp() now. I've been writing something
         | like the OP all this time [?]
        
         | brandmeyer wrote:
         | C++17 does have it... but it doesn't compile optimally. It
         | compiles to something based on comparisons instead of the
         | floating-point-specific operations. I tried this on a number of
         | compiler combinations and didn't see anything that would emit
         | min/max instructions for `std::clamp<double>`.
         | 
         | https://www.godbolt.org/z/MKqTvE
        
           | ChrisLomont wrote:
           | Your two functions are not equivalent. std::fmax handles NaN,
           | but std::clamp does not.
        
             | brandmeyer wrote:
             | Depends on how the comparisons are ordered. Some of the
             | orderings I've seen in here do respect NaN by virtue of `x
             | > upper_bound` comparing false if either x or upper_bound
             | are NaN.
        
         | f00zz wrote:
         | It gets a bit confusing when the order of arguments is
         | different depending on the library. For instance, with std it's
         | std::clamp(val, min, max), but with Qt it's qBound(min, val,
         | max) (for some reason I think the order of arguments in qBound
         | is more logical).
        
           | Someone wrote:
           | The expression _Math.min(Math.max(num, min), max)_ is
           | symmetric in _min_ and _num_ , so it doesn't matter whether
           | you interchange _min_ and _num_ (or, for that matter, _max_
           | and _num_ , but that is harder to see from that way to define
           | the 'clamp' function)
        
             | f00zz wrote:
             | Good point, thanks.
             | 
             | (Though I've just checked gcc's definition of std::clamp
             | and it looks like                   __glibcxx_assert(!(__hi
             | < __lo));         return (__val < __lo) ? __lo : (__hi <
             | __val) ? __hi : __val;
             | 
             | so order of arguments does matter there.)
        
           | wruza wrote:
           | It depends. (val, min, max) operates on a first argument,
           | which is more logical as well. (min, max, val) allows range
           | constants to be more visible if <val> is a lenghty
           | expression. In more powerful languages like objective-c this
           | has less sense, as you can always specify all arguments
           | explicitly:                 [FZUtilityManager
           | clampIntegerValue:(NSInteger)x
           | toRangeWithLowerBound:(NSInteger)min
           | upperBound:(NSInteger)max
           | withError:(out NSError **)error];
           | 
           | Which returns NSIntegerMax and sets the error variable if the
           | range appears to be empty. The chance that max is
           | NSIntegerMax is low, but if your data allows that, you can
           | always put an additional shortcut before clamping.
           | if (min > max) {           error = [NSError
           | errorWithDescription:@"min > max occured"];           return
           | NO; // or equivalent       } else {           x = ...       }
           | // use x
        
           | nemetroid wrote:
           | In Haskell, functions often take their arguments in the order
           | that makes the most sense to partially apply. In this case,
           | that would probably be clamp(min, max, val): supplying the
           | first two arguments results in a reusable clamping function.
        
         | Semaphor wrote:
         | > Modern C# has Math.Clamp() since .NET Core 2.0; too bad it's
         | not available in desktop edition of the runtime.
         | 
         | Huh, that's good to know. It's also in .net standard 2.1. A
         | shame it wasn't added to Framework 4.8 (which I guess is what
         | you mean by "desktop edition of the runtime"?)
        
         | gameswithgo wrote:
         | > in desktop edition of the runtime
         | 
         | huh?
        
           | Const-me wrote:
           | MS .NET framework. Unfortunately, for the last couple years
           | it lags behind .NET core. Even 2 years old .NET core 2.1 is
           | better in some regards than the latest desktop version 4.8.
        
             | wozer wrote:
             | .NET 5 will be based on .NET Core. It's already in preview.
        
         | the-dude wrote:
         | Great. But I have been programming since about 1985.
        
           | Const-me wrote:
           | I've been programming for living since 2000, but I don't
           | think that's relevant. No reason not to use what's available
           | in standard libraries of whatever language you're writing.
           | 
           | For example, C++ on AMD64 is very likely to compile
           | std::clamp<double> into 2 instructions, minsd and maxsd. I'm
           | not so sure about nested ternaries mentioned elsewhere in the
           | comments.
        
             | nickysielicki wrote:
             | https://godbolt.org/z/bq5E8h
             | 
             | It's actually the std::min/std::max version that goes to
             | minsd/maxsd in both clang/gcc.
        
             | naikrovek wrote:
             | There are very good reasons to avoid std:: stuff.
             | 
             | And if you don't already know that in your soul, I will
             | appear to be a genuine crackpot, and the reasons not to use
             | std::* will still exist.
        
               | Const-me wrote:
               | I think you're overgeneralizing here.
               | 
               | I'm aware some parts of C++ standard library are outright
               | horrible, like iostream and I/O in general. Other parts
               | are questionable, like date & time, locales, and futures.
               | 
               | Meanwhile, other parts of the same standard library are
               | actually OK (most collections, threading,
               | synchronization, atomics, smart pointers, initializer
               | lists). And other parts are awesome, like most of the
               | stuff from <algorithm> header.
               | 
               | Apparently, one of the C++ design goals was to not pay
               | for features which aren't used. Selectively ignoring
               | stuff from the standard library doesn't have much
               | downsides.
        
               | kcb wrote:
               | Are you saying a C++ developer shouldn't use the standard
               | library?
        
               | klyrs wrote:
               | Many performance-critical C++ programmers treat std with
               | suspicion. One thing to keep in mind is that the
               | interface is standard, but the implementations are not,
               | and can foil you on cross-platform development. Another
               | is that you might not need everything that a std
               | container provides, and you can get away with a
               | streamlined data structure that doesn't support those
               | unnecessary operations.
               | 
               | But as a sibling commenter notes... this isn't relevant
               | for min/max.
        
               | kcb wrote:
               | I'd much rather work on an application that utilized the
               | standard lib than one that brought in dependencies and
               | custom data structures.
               | 
               | If it really is a bottleneck on a hot path then go for
               | it. But not using it because of some ancient anecdotes is
               | going to lead to an unmaintainable mess.
        
               | Tuna-Fish wrote:
               | Not the person you are replying to, but "a lot of the
               | standard library is horrifying and should never be
               | touched" is pretty standard advice for C++. Mind, I don't
               | think this particular function belongs to that set.
        
           | HugoDaniel wrote:
           | Great. But I have been programming since about
           | Math.min(Math.max(n, 1900), 1985 - 1)
        
       | Imnimo wrote:
       | On the one hand, I've done this enough times that I can usually
       | write it on auto-pilot without thinking about it. But on the
       | other hand, whenever I make a mistake, the resulting bug is
       | really hard to track down, because it's tough to just stare at
       | the line and recognize that something wrong.
        
       | fouric wrote:
       | Do people actually have trouble with this repeatedly, or just
       | when they first learn about it? I started using this
       | implementation of clamp a few years ago, and while it gave me
       | some trouble when I first implemented it, the pattern is very
       | simple, and I got used to it very quickly.
       | 
       | I use Common Lisp:
       | 
       | > (max min (min max n))
       | 
       | Is the difference my choice of language, my personal mental
       | hardware, the amount of familiarity one has with the pattern, or
       | none of the above?
        
       | ragnese wrote:
       | This one doesn't bother me much. What's harder and worse, IMO, is
       | doing saturating _math_.
        
       | edparcell wrote:
       | It is simpler if you use the notation [x]_a^b (i.e. with a
       | subscript and a superscript b) to mean x, clipped to the range a
       | to b, and skip writing +/- infinity if you don't intend clipping
       | on one side.
       | 
       | Then you get a bunch of obvious identities like [x]^b = min(x, b)
       | = [b]^x (x capped by b is the same as the smaller of x and b
       | which is the same as b capped by x), [x]_a^b = [b]_a^x, and
       | [x]_a^b = [[x]_a]^b. Putting these together you get [x]_a^b =
       | [[x]_a]^b = min(max(x, a), b). But honestly it's just easier to
       | stick to the notation most of the time.
       | 
       | A better write-up, for everyone who doesn't like reading new math
       | notations inline: https://imgur.com/gallery/593QEow (Imgur link
       | with white background)
       | https://quicklatex.com/cache3/71/ql_46c49ac709b3789482d0736d...
       | (Original link - renders badly in Chrome due to PNG transparency)
        
         | secondcoming wrote:
         | Those are all valid C.
        
       | tobr wrote:
       | [min, num, max].sort()[1]
        
         | tosh wrote:
         | sort() on JavaScript arrays sorts alphabetically (unless you
         | pass a compareFunction)
         | 
         | https://stackoverflow.com/questions/21019902/why-cant-javasc...
         | 
         | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
        
           | tobr wrote:
           | Very good point. I thought of it more as pseudocode.
        
             | tosh wrote:
             | imho a very elegant solution and probably works in most
             | languages, I was surprised by JavaScript's behaviour myself
        
               | 1-more wrote:
               | For a real fun headscratcher consider [ '9', '10', '11',
               | '12', '13' ].map(parseInt)
        
               | yboris wrote:
               | Thank you for this exercise! I figured it out!
               | 
               | [ROT 13] spoiler: cnefrVag frpbaq nethzrag vf onfr, znc
               | frpbaq nethzrag vf vaqrk va neenl. 10 vf gur frpbaq
               | nethzrag, vaqrk 1. cnefrVag(10, 1) unf vyyrtny onfr
               | 
               | [0] https://rot13.com/
        
           | tgb wrote:
           | Wait, really? I just had to double-check my one JS project
           | for bugs and either I used to know this gotcha or I got
           | lucky. My sort()-ing needed to handle NaNs carefully so I was
           | already using custom comparators.
        
           | yesbabyyes wrote:
           | Good point, for those who need to implement a slick
           | compareFunction for numbers, Math.sign is great:
           | [min, num, max].sort((a, b) => Math.sign(a - b))[1]
           | 
           | An entirely different alternative is poor man's match:
           | switch (true) {       case num > max: return max;       case
           | num < min: return min;       default: return num;       }
        
             | recursive wrote:
             | Why sign though? I always write my sorts like this.
             | .sort((a, b) => a - b)
        
               | saagarjha wrote:
               | Is there a better way to grab a reference to the
               | subtraction operator?
        
               | recursive wrote:
               | I assume you're referring to a point-free reference.
               | 
               | Only by defining a `const subtract = (a, b) => a - b;`,
               | which isn't really point-free.
               | 
               | Or by using a different language.
        
           | TimTheTinker wrote:
           | "lexicographically" is a better descriptor than
           | "alphabetically", since it's sorting by UTF-8 code point
           | value.
        
             | edflsafoiewq wrote:
             | It sorts by UTF-16 code unit.
        
               | TimTheTinker wrote:
               | Of course, my bad.
        
         | seanwilson wrote:
         | It's cute but if you used this in an innerloop (like a game,
         | simulation or graphics code where 'clamp' is used often), it'll
         | generate a ton of garbage as well as potential slowdown for no
         | good reason.
        
           | tobr wrote:
           | This should be completely obvious, but largely irrelevant to
           | the topic at hand. The linked tweet talks about how difficult
           | it is to remember the order of the terms when you implement
           | clamp a certain way; I just wanted to point out that with a
           | different solution, the order surprisingly doesn't matter at
           | all.
        
             | seanwilson wrote:
             | I don't think it's obvious though - many people have never
             | optimised for garbage collection so I thought it was worth
             | pointing out.
             | 
             | I still found your approach interesting and it's rare
             | there's a perfect approach so don't take it to heart.
        
         | agumonkey wrote:
         | never thought of clamp as precompiled sort
        
         | unoti wrote:
         | [min, num, max].sort()[1]
         | 
         | I really love this! I'm not sure whether to laugh, cry, or
         | applaud, but I love how it makes me feel all those emotions at
         | the same time.
        
           | city41 wrote:
           | I also think the fact it doesn't work in JS (due to sort
           | defaulting to alphabetical sort) brings in even more
           | appropriate emotions.
        
             | sanbor wrote:
             | [10, 2, 100].sort()[1] //returns 100
        
         | vortico wrote:
         | This is also nice because it gracefully handles the case where
         | `max < min`.
        
           | rags2riches wrote:
           | It's not always graceful to say that 1 is both less than 0
           | and greater than 2.
        
             | vortico wrote:
             | Not sure what you mean. If I want to clamp 1 between 2 and
             | 0, the most reasonable answer is 1, which is correctly
             | returned by this code.
        
               | resu_nimda wrote:
               | He's saying that, if you have explicitly defined a max
               | and min such that max < min, it is not graceful for the
               | computer to produce a result as though those values were
               | swapped. In other words, garbage in should produce
               | garbage out.
               | 
               | The array implementation sidesteps this by not
               | semantically defining a max and min, instead sorting
               | three arbitrary numbers.
        
               | tobr wrote:
               | In practice "max" and "min" often aren't conceptually
               | important for a clamp function. It's more that you want
               | to keep a value within a range, which is defined by two
               | end points in arbitrary order.
               | 
               | If that is the version of clamp you need, the _sort_
               | based solution reveals something profound and unexpected:
               | it's not just the two end points of the range that are
               | equivalent, but _all three numbers_. Keeping value A
               | between B and C is the same as keeping B between A and C
               | or C between A and B. It's completely arbitrary which
               | pair you consider to be a range.
        
               | vortico wrote:
               | Why do you prefer to leave undefined behavior?
        
               | resu_nimda wrote:
               | I'm not sure what you mean. The behavior of the max() and
               | min() functions is perfectly well defined. The terms
               | "maximum" and "minimum" are well defined. If I were using
               | those terms I would likely consider the case where max <
               | min to be an error, or have some other meaning, like an
               | empty range.
               | 
               | If I wanted it to automatically flip the values to ensure
               | a sensible range is defined, I would probably use "a" and
               | "b" or "endpoint1" and "endpoint2" or something, because
               | "max" has now become "max or min," which is not the same.
        
         | throwawaw wrote:
         | I love this one.
        
         | gridlockd wrote:
         | This is why computers are slow.
        
           | secondcoming wrote:
           | No. Just things written by web devs.
           | 
           | (Shots fired)
        
           | saagarjha wrote:
           | I was curious how slow this would be, and here is what
           | JavaScriptCore made of this code:                 function
           | clamp(n, min, max) {           return [min, n, max].sort((a,
           | b) => a - b)[1];       }              for (var i = 0; i <
           | 10000000; i++) {           clamp(Math.random() | 0,
           | Math.random() | 0, Math.random() | 0);       }
           | 
           | However, I was pretty disappointed when it seemed to be
           | calling sort each time :( Perhaps I profiled it incorrectly?
           | jsc's profiling data shows that it never hit FTL and nothing
           | ever got inlined. The bytecode for DFG and Baseline is
           | identical:                 Compilation
           | clamp#CChm91-1-Baseline:             arg0: predicting
           | OtherObj             arg1: predicting BoolInt32
           | arg2: predicting BoolInt32             arg3: predicting
           | BoolInt32               [   0] enter
           | [   1] get_scope          loc4               [   3] mov
           | loc5, loc4               [   6] check_traps
           | [   7] mov                loc11, arg2               [  10]
           | mov                loc12, arg1               [  13] mov
           | loc13, arg3               [  16] new_array          loc10,
           | loc11, 3, 3               [  22] get_by_id          loc7,
           | loc10, 0               [  27] new_func_exp       loc9, loc4,
           | 0               [  31] call               loc7, loc7, 2, 16
           | [  37] get_by_val         loc6, loc7, Int32: 1(const0)
           | [  42] ret                loc6            Compilation
           | clamp#CChm91-2-DFG:             arg0: predicting OtherObj
           | arg1: predicting BoolInt32             arg2: predicting
           | BoolInt32             arg3: predicting BoolInt32
           | [   0] enter                             [   1] get_scope
           | loc4               [   3] mov                loc5, loc4
           | [   6] check_traps                       [   7] mov
           | loc11, arg2               [  10] mov                loc12,
           | arg1               [  13] mov                loc13, arg3
           | [  16] new_array          loc10, loc11, 3, 3               [
           | 22] get_by_id          loc7, loc10, 0               [  27]
           | new_func_exp       loc9, loc4, 0               [  31] call
           | loc7, loc7, 2, 16               [  37] get_by_val
           | loc6, loc7, Int32: 1(const0)               [  42] ret
           | loc6
        
             | garmaine wrote:
             | Dude, you are generating random numbers inside the loop.
             | That is going to dominate runtime.
        
               | saagarjha wrote:
               | I'm not testing runtime, though, I'm testing whether
               | clamp gets optimized.
        
             | [deleted]
        
       | V-2 wrote:
       | Kotlin provides pretty nice syntax sugar for that:
       | num.coerceIn(min..max)
       | 
       | That's it. This human reader finds it considerably more readable.
       | 
       | It also has                  coerceAtLeast
       | 
       | and                  coerceAtMost
        
         | sanbor wrote:
         | This is what I love about kotlin. The standard library has a
         | good coverage of these kinds of problems and it is done in a
         | simple an clean way. I had the same feeling with Python, the
         | language got you cover with mundane tasks and you don't have to
         | spend time researching libs that do it for you (or spend your
         | time doing a clamp implementation + tests).
        
         | nitrogen wrote:
         | Does kotlinc optimize away inline ranges like that, or does
         | this result in a range object being constructed and discarded?
        
       | occamrazor wrote:
       | Numpy has `np.clip`.
       | 
       | In pure Python `statistics.median([min, max, num])` also works.
        
       | slifin wrote:
       | I'm not sure why the author isn't putting his preferred
       | implementation into a clamp function then forgetting about it?
        
       | LifeLiverTransp wrote:
       | communicate by structure what it does.. clamp(lowerBound, val,
       | upperBound)
        
       | kaiken1987 wrote:
       | An amusing anecdote ruined by the fact that everyone who can
       | relate immediately starts thinking of a better way to rewrite it.
       | Yes brain that might be better but that's not the joke.
        
       ___________________________________________________________________
       (page generated 2020-08-20 23:00 UTC)