Math.min(Math.max(num, min), max)
twitter.com
twitter.com
I find the following easier to read :
Math.min(Math.max(num, lower_bound), upper_bound) 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); @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.near get(), post(), and ajax()
// we alias at import
// to keep all the code short
min(max(number, min), max)
Cryptic sorcerer!
"Math.min(Math.max(v, min), max);"
his incantation.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.
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)
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.
I sometimes mix up "min" as "take the minimum" rather than "take the larger given this minimum".
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.
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)
I did exactly this for my PhD!
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.
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.
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.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;"
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.
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.
By definition, something that is not a number (real, integer, etc.) cannot be compared to something that is a number.
>>> 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. np.clip(ndarray, lower_bound, upper_bound)
And handles NaNs correctly.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]
Recording the lack of a value is what null is for.
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.
clamped(num, range=[ lower_bound, upper_bound ]);
and then have no interest in how this actually gets implemented. [100, 10, 11].sort()[1] === 100BRB, just checking some code…
Math.max(lower_bound, Math.min(num, upper_bound))
Since i read right to left.
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); 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
}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.
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.
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.
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.
But as a sibling commenter notes... this isn't relevant for min/max.
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.
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.
It's actually the std::min/std::max version that goes to minsd/maxsd in both clang/gcc.
> Fout bij het maken van de databaseconnectie
(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.) [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 xThe list of hard programming things is long; things that are trivially solved by a tool shouldn't be in the list.
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"?)
huh?
25.clamp(5, 10)
=> 10
6.clamp(5, 10)
=> 6
1.clamp(5, 10)
=> 5squish(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.
https://repl.it/repls/GargantuanThistleLink
Result from sort: 3 in 0.9785124980007822s
Allocated 3000001 object(s)
Result from ternary: 3 in 0.3205206830025418s
Allocated 1 object(s)
Result from clamp: 3 in 0.5030354310001712s
Allocated 2 object(s)
Interestingly the ternary comparison is faster than clamp.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))
OTOH I think chained ternaries can be simple and easy to understand.
Yes, they are the exact same thing in this case, but getting rid of those nested parens really helps, at least for me.
sarah180's example is a good illustration. I would change the order of the tests because it makes more sense to me to check the min before the max. I'd also make one minor formatting change, because I code in a proportional font and can't line things up in columns:
a < min ? min :
a > max ? max :
a
Maybe people think differently, but to me that is super easy to understand, and much better than the confusing Math.min/max stuff.I would also wrap the whole thing inside a function:
function clamp( value, min, max ) {
return(
value < min ? min :
value > max ? max :
value
);
}
Now that it's inside a function, you could change the code to use if statements, or Math.min/max, or whatever suits your preferences. a > max ? max :
a < min ? min :
a # 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...
If I just say "sub d1, d2", of course I mean the whole register width, and not just 16 or 8 bits of it.
num < min ? min :
num > max ? max :
num 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.Junior developer puts the one liner on google, finds the typical macro definition and does the change.
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
This is one of those cases where I think it is much more readable to just write the code than to puzzle over what Math.min(Math.max(min, num), max); might be doing.
if (num < min)
return min;
if (num > max)
return max;
return num;
That's how I'd write it. May not be super terse, but anyone that stumbles on this will know precisely what's happening without needing to take a few seconds to puzzle things out.In Javascript in my browser (Safari) when these methods get called enough to get compiled using the most aggressive stage of the JIT, they end up essentially the same speed. On my laptop either one runs about 145 million times per second on one CPU core.
I wouldn’t be surprised if a C compiler ended up making this function significantly faster, but I haven’t tested it.
num.coerceIn(min..max)
That's it. This human reader finds it considerably more readable.It also has
coerceAtLeast
and coerceAtMostLuckily, these convenience methods are usually implemented as inline extension functions, so the whole thing will get inlined into the calling method, making JIT optimization more likely.
[min, num, max].sort()[1]https://stackoverflow.com/questions/21019902/why-cant-javasc...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
[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
[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;
} .sort((a, b) => a - b)Only by defining a `const subtract = (a, b) => a - b;`, which isn't really point-free.
Or by using a different language.
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.
[10, 2, 100].sort()[1] //returns 100The array implementation sidesteps this by not semantically defining a max and min, instead sorting three arbitrary numbers.
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.
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.
(Shots fired)
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 loc6I still found your approach interesting and it's rare there's a perfect approach so don't take it to heart.
In Elixir which does have the pipe `|>` this could be.
number = number |> max(lower_bound) |> min(upper_bound)
Which IMO is quite elegant.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)
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. : clamp ( x min max -- y ) [ max ] dip min ; inline
https://docs.factorcode.org/content/word-clamp,math.order.ht...So this pops the max value off the stack, applies the quoted `max` word to the x and min stack values, then pops the max value back on the stack and applies the `min` word to the result and the max.
if num > max: num = max
if num < min: num = min
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... function clamp(num, min, max) {
if (num > max)
return max;
if (num < min)
return min;
return num;
}That doesn't seem unreasonable. (In fact, that's what happens with the min/max approach).
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};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...
clamp(null) returns 0
clamp(undefined) returns NaN
clamp(1, NaN, NaN) returns 0
clamp(1) returns 0
clamp(1, 5, NaN) returns 5
JavaScript is hard to write safe code for.
require('clamp')(num, min, max)
1 million monthly downloads 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...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.
(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.) (> 3 2 1 ...)
vs (3 > 2) && (2 > 1) && ... (-> n
(max vmin)
(min vmax))I felt the same for awhile but now I just mentally put the operator between the operands, so (> 3 2) is the same as 3 > 2.
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?
xs: 1 2 3 4 5
max: 4
min: 2
max& min| xs
2 2 3 4 4
works for scalar, vector, matrix max <. min >. nums
or equivalently: min >. max <. numsleft_of_U ( right_of_L (num)) = right_of_L ( left_of_U (num)) = clamped version of num between L and U.
Here, left_of_U = Math.min(num, U) and right_of_L = Math.max(num, L).
lower⌈ upper⌊ numbers
See a stream of random numbers flowing from right to left. See the higher ones being pushed down ⌊ to the upper bound, and the lower ones being pushed up ⌈ to the lower bound, and the middle ones flowing through both guards unchanged. =MEDIAN(min, num, max)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]
val - (sign(val - max) + 1) / 2 * (val - max) + (sign(min - val) + 1) / 2 * (min - val) 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 (min + ((num + max - Math.abs(num - max)) / 2) + Math.abs(min - ((num + max - Math.abs(num - max)) / 2))) / 2
Since min(a, b) = (a + b - |a - b|) / 2
and max(a, b) = (a + b + |a - b|) / 2 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. 5 9 7 min max .
This prints 7, as it's between 5 (lower bound) and 9 (upper bound).Defining a clamp "function" looks like this:
: clamp ( min max n -- clamped_n )
min max ; 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: nMaybe 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 benefits1.) 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)
Doesn't that make writing functions that can use partial application harder? e.g. If I was writing clamp i would want the signature to be
(defn clamp [min max n] ,,,)
Then I can do: (map (partial clamp 1 11) [-14 2 5 8 11 15 18])
I know when I use Clojures threading macros I use thread last way more than any of the others. My next most common would be piping it into arbitrary locations, e.g.: ; pipe into an arbitrary spot (specified here as o)
(as-> (range 1 10) o
(map inc o)
(filter even? o)
(reduce + o))
I rarely use thread-first. &Compare.clamp(&1, some_min, some_max)
The above creates an arity one function which puts the arg into the first position in the original function.I've never felt pain around partial application because I use the anonymous function short hand a good deal
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
endIn pure Python `statistics.median([min, max, num])` also works.
upperbound = min
lowerbound = max
Use: a = upperbound(a, 10)
b = lowerbound(-10, b)> Googler
Then this is another proof that google doesnt only hire on mathematical puzzle tricks
Math.max(b, x) == at_least_b(x)
at_most_b(at_least_a(x))
A similar kind of thing is really common in measure theory (limsup, liminf) and option payoffs.
clamped = num `max` min' `min` max'So
a `foo` b `bar` c
is (bar (foo a b) c)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.
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
In elixer that could be
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)
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.
Not in my experience. The compiler is likely to be able to do something decent to it, but it'll probably be different.
Consider the following snippets. For unsigned integers they're all equivalent (and return the max of x and m), but gcc with a wide range of flags can't recognize them as identical.
(x<m) * m + (x>=m) * x
(x<m) * (m-x) + x
x<m ? m : xInterestingly in your example, clang is able to resolve the first and the third line to the same assembler code.
I do wonder why it then isn't able to do that for the second line. Possibly because the CPU might have different flags set after processing the substraction?
But I'm not sure if your example is a good argument after I said I prefer less confusing code and you present an example which even confuses the compiler. ;-)
Is that supposed to be difficult?
Not trying to be snarky, I'm honestly surprised, do people actually struggle with this? Googlers in particular?
While doing competitive programming I learned that I'm really stupid, but even as stupid as I am, I still understand that expression easily.
So never use it in code.
There are probably SQL devs reading this and wondering what the fuss is about.