On Thu, Aug 4, 2016 at 6:20 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Think about why you're clamping. It's unlikely to be used just once, for
a single calculation. You're likely to be clamping a whole series of
values, with a fixed lower and upper bounds. The bounds are unlikely to
be known at compile-time, but they aren't going to change from clamping
to clamping. Something like this:

lower, upper = get_bounds()
for x in values():
    y = some_calculation(x)
    y = clamp(y, lower, upper)

is the most likely use-case, I think.

I was curious about what the likely cases are in many cases, so I ran a quick sample from a professional project I am working on, and found the following results:

clamping to [0, 1]: 50 instances, almost always dealing with percentages
lower is 0: 44 instances, almost all were clamping an index to list bounds, though a few outliers existed
lower is 1: 4 instances, 3 were clamping a 1-based index, the other was some safety code to ensure a computed wait time falls within certain bounds to avoid both stalling and spamming
both values were constant, but with no real specific values: 11 instances (two of these are kinda 0,1 limits, but a log is being done for volume calculations, so 0 is invalid, but the number is very close to 0)
one value was constant, with some arbitrary limit and the other was computed: 0
both values were computed: 20 instances (many instances have the clamping pulled from data, which is generally constant but can be changed easier than code)

Any given call to clamp was put into the first of the categories it matched. "computed" is fairly general, it includes cases where the value is user-input with no actual math done.

As would be expected, all cases were using computed value as the input, only the min/max were ever constant.

The project in this case is a video game's game logic code, written in C#. None of the shaders or engine code is included. There may be additional clamping using min/max combinations, rather than the provided clamp helpers that were not included, however the search did find two instances, where they were commented as being clamps, which were included.

Basically all of the cases will repeat fairly often, either every frame, move, or level. Most are not in loops outside of the frame/game loop.
If lower happens to be greater than upper, that's clearly a mistake. Its
better to get an exception immediately, rather than run through a
million calculations and only then discover that you've ended up with a
million NANs. It's okay if you get a few NANs, that simply indicates
that one of your x values was a NAN, or a calculation produced a NAN.
But if *every* calculation produces a NAN, well, that's a sign of
breakage. Hence, better to raise straight away.

I personally don't have much opinion on NAN behaviour in general - I don't think I've ever actually used them in any of my code, and the few cases they show up, it is due to a bug or corrupted data that I want caught early.