Description
Intro
clamp(x, xmin, xmax)
is a function that clamps (or, clips) an input scalar or array x
so that the result is within given bounds xmin
and xmax
. For example:
res = clamp(0.9. 0.2, 0.8) ! res is 0.8
res = clamp(0.7. 0.2, 0.8) ! res is 0.7
res = clamp(0.1, 0.2, 0.8) ! res is 0.2
This function is common in standard libraries. I've used it occasionally in Fortran, and I use it often in Python. It provides an easy way to ensure that a variable with a finite (non-NaN, non-Inf) value stays within bounds.
Name
Python has numpy.clip
, while C++, Julia, and Rust all have clamp
. So clamp
is more common. Both clamp
and clip
are equally meaningful to me. I have slight preference for clip
because it's shorter and has a "sharper" sound to it.
What inspired me to open this proposal is reading that clamp
was stabilized in Rust 1.50.0.
API
elemental real function clamp(x, xmin, xmax) result(res)
real, intent(in) :: x ! input value to clip
real, intent(in) :: xmin ! lower bound
real, intent(in) :: xmax ! higher bound
Alternative names for the bounds could be low
and high
.
Implementation
There are a few options that I know of, each of which produce different assembly instructions, and performance depending on optimization levels, array sizes, and whether the clamping is in a loop or over a whole array in a single call.
A min/max clamp
This is the simplest implementation I could think of:
elemental real function clamp(x, xmin, xmax) result(res)
real, intent(in) :: x, xmin, xmax
res = min(max(x, xmin), xmax)
end function clamp
There are two intrinsic function calls here, but maybe a good compiler will optimize one away. I don't know.
An if/then/else clamp
elemental real function clamp(x, xmin, xmax) result(res)
real, intent(in) :: x, xmin, xmax
if (x < xmin) then
res = xmin
else if (x > xmax) then
res = xmax
else
res = x
end if
end function clamp
A branchless clamp
This avoids branching but introduces a floating-point error (~epsilon(x)
) for some elements, even if x is not clipped.
elemental real function clamp(x, xmin, xmax) result(res)
! A branchless clamp.
! Credit: Mark Ransom (https://stackoverflow.com/a/7424900/827297)
real, intent(in) :: x, xmin, xmax
res = (x + xmin + abs(x - xmin)) / 2
res = (res + xmax - abs(res - xmax)) / 2
end function clamp
What module would this go to?
I don't think we have an appropriate existing module yet, but stdlib_numeric
or stdlib_math
sound meaningful to me.