Is there a more efficient way to clamp real numbers than using if statements or ternary operators? I want to do this both for doubles and for a 32-bit fixpoint implementation (16.16). I'm not asking for code that can handle both cases; they will be handled in separate functions.
Obviously, I can do something like:
double clampedA; double a = calculate(); clampedA = a > MY_MAX ? MY_MAX : a; clampedA = a < MY_MIN ? MY_MIN : a;
or
double a = calculate(); double clampedA = a; if(clampedA > MY_MAX) clampedA = MY_MAX; else if(clampedA < MY_MIN) clampedA = MY_MIN;
The fixpoint version would use functions/macros for comparisons.
This is done in a performance-critical part of the code, so I'm looking for an as efficient way to do it as possible (which I suspect would involve bit-manipulation)
EDIT: It has to be standard/portable C, platform-specific functionality is not of any interest here. Also, MY_MIN
and MY_MAX
are the same type as the value I want clamped (doubles in the examples above).
Suppose we wanted to limit an integer to stay within some arbitrary range, like 0-50. This can be easily achieved by testing the current value with an if statement and assigning the maximum or minimum value accordingly.
Description. Clamps the given value between the given minimum float and maximum float values. Returns the given value if it is within the minimum and maximum range. Returns the minimum value if the given float value is less than the minimum.
std::clamp in C++ 17 Clamps a variable to a given range[high – low]. If num > high, num is assigned high. If num < low, num is assigned low. If num is already clamped, no modifications.
A C-clamp or G-clamp or G-cramp is a type of clamp device typically used to hold a wood or metal workpiece, and often used in, but are not limited to, carpentry and welding.
Both GCC and clang generate beautiful assembly for the following simple, straightforward, portable code:
double clamp(double d, double min, double max) { const double t = d < min ? min : d; return t > max ? max : t; }
> gcc -O3 -march=native -Wall -Wextra -Wc++-compat -S -fverbose-asm clamp_ternary_operator.c
GCC-generated assembly:
maxsd %xmm0, %xmm1 # d, min movapd %xmm2, %xmm0 # max, max minsd %xmm1, %xmm0 # min, max ret
> clang -O3 -march=native -Wall -Wextra -Wc++-compat -S -fverbose-asm clamp_ternary_operator.c
Clang-generated assembly:
maxsd %xmm0, %xmm1 minsd %xmm1, %xmm2 movaps %xmm2, %xmm0 ret
Three instructions (not counting the ret), no branches. Excellent.
This was tested with GCC 4.7 and clang 3.2 on Ubuntu 13.04 with a Core i3 M 350. On a side note, the straightforward C++ code calling std::min and std::max generated the same assembly.
This is for doubles. And for int, both GCC and clang generate assembly with five instructions (not counting the ret) and no branches. Also excellent.
I don't currently use fixed-point, so I will not give an opinion on fixed-point.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With