Live data from Hacker News

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

twitter.com

281–290 of 291 posts

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

#282

Earlier quoted context omitted.

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)

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

Reading that code it looks to me that:

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.

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

#283
Slightly offtopic, but very practical use of a comparable algorithm is to determine to what extent a date ranges falls between a first and last date (e.g. first date of the year, last date of year). e.g. in Excel it would look like: MAX((MIN(end date range; last date) - MAX(Start date range;first date) + 1);0)/(last date - first date + 1). This results in 0 in case of no match, a fraction when only a part of the date range falls between the first and last date, and 1 in case it matches completely or even exceeds the first and last date.

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

#284
post #278

Earlier quoted context omitted.

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)

That would depend on what you do with the NaNs. For instance I have been using them extensively in time series data representation to denote a specific entry has no value - think of Saturday and stock/forex markets.

That's not what NaN means. It means there is a value, and that value is... Check this out... "Not a Number".

Recording the lack of a value is what null is for.

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

#285
post #45

Earlier quoted context omitted.

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

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)

Self reply: Oops. That ‘for that matter’ part isn’t true, as it would mean you could permute the arguments any way you wished.

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

#286
post #278

Earlier quoted context omitted.

That would depend on what you do with the NaNs. For instance I have been using them extensively in time series data representation to denote a specific entry has no value - think of Saturday and stock/forex markets.

That's not what NaN means. It means there is a value, and that value is... Check this out... "Not a Number". Recording the lack of a value is what null is for.

[deleted]

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

#287
post #278

Earlier quoted context omitted.

That would depend on what you do with the NaNs. For instance I have been using them extensively in time series data representation to denote a specific entry has no value - think of Saturday and stock/forex markets.

That's not what NaN means. It means there is a value, and that value is... Check this out... "Not a Number". Recording the lack of a value is what null is for.

Null does not pertain to primitive types in java and in C - it'd be zero when applied to a 'double'. (note: you want double[] as backing storage in java and you absolutely do not indirections). Aside that I have quite a good idea how NaN is represented internally and what it does, e.g. you can have several different NaNs that have different representation bitwise. Baring that - NaNs are pretty decent to represent lack of value as all operations with them result into a NaN. In the end NaN is just a composition of bits that the hardware can optimize for.

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

#288

Earlier quoted context omitted.

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.

Ideally the temporary array should optimise away.

Ideally, but so far my experiences with simple benchmarks and with ruby-prof have shown that CRuby doesn't make such optimizations:

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.

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

#289
post #45

Earlier quoted context omitted.

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

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.

It would be nice if partial application worked with keyword arguments. Actually it would be great if Haskell natively supported keyword arguments.

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

#290
post #263
post #12

Earlier quoted context omitted.

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…

> 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) * x (x

There can always be things that trip the compiler. Without looking at the actual assembler, you never know that.

Interestingly in your example, clang is able to resolve the first and the third line to the same assembler code.

https://godbolt.org/z/navMEc

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

Post reply on HN