Live data from Hacker News

Math.min(Math.max(num, min), max)

twitter.com

131–140 of 291 posts

Re: Math.min(Math.max(num, min), max)

#131
post #128

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...

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.

Re: Math.min(Math.max(num, min), max)

#132
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

Re: Math.min(Math.max(num, min), max)

#133
post #4

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.

If fastest poll rate > slowest poll rate, I think you've got them the wrong way around (or is that the joke?).

It seems like they are just have bad names, as their unit probably is seconds not 1/seconds.

Re: Math.min(Math.max(num, min), max)

#134
post #75
post #28

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)

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)

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.

Re: Math.min(Math.max(num, min), max)

#135

>[...] 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?

Took me a few minutes first time I saw it but it's now firmly in my mental library of idioms.

Re: Math.min(Math.max(num, min), max)

#136

Here's clamp in idiomatic Elixir (using multi-clause functions and guards): def clamp(min, _max, n) when n max, do: max def clamp(_min, _max, n), do: n

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.

Re: Math.min(Math.max(num, min), max)

#137
post #128

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...

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.)

Re: Math.min(Math.max(num, min), max)

#138
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.

Re: Math.min(Math.max(num, min), max)

#139

Earlier quoted context omitted.

It's not always graceful to say that 1 is both less than 0 and greater than 2.

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.

He's saying that, if you have explicitly defined a max and min such that max The array implementation sidesteps this by not semantically defining a max and min, instead sorting three arbitrary numbers.

Re: Math.min(Math.max(num, min), max)

#140
post #121

Earlier quoted context omitted.

> 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/

Wait, how is that not trivial?

> 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.

Post reply on HN