I found two questions here regarding fast and/or elegant clamping of numbers in C/C++. The C one focuses on (potentially platform specific) optimization:
Fastest way to clamp a real (fixed/floating point) value?
The C++ one is also related to C:
Most efficient/elegant way to clip a number?
However, none of these mention if it is about an in-place operation or not, which seems to be a crucial piece of information to me. This question is about an in-place operation. The difference is that if the number is within the bounds, no write operation needs to take place.
When browsing through answers regarding clamping in general, most people just recommend to use the ternary variant, no matter what. I am wondering why is this the case? Both in terms of speed and readability, this is not clear to me, in particular for the in-place case. I would like to understand if there is a best practice and if so, I would be interested in a justification.
In my example code, there are three variants: The activated variant (1) is how I usually implement it. Variant (2) uses an else if
and variant (3) uses the ternary operator.
According to measurements on an some i5 CPU and gcc with -O3
, for TYPE
being float
and double
, (1) is the fastest, (2) and (3) are clearly slower and achieve approximately the same speed compared to each other. For int
, all variants achieve approximately same speed.
I tagged this question explicitly as C and not C/C++, because in the latter case - besides being a duplicate - people would answer to use std::clamp
or Boost, which is not the idea of this question. This is about the best practice for an own platform-independent assembly free implementation, both in terms of speed and readability.
Side note, even though C++: using std::clamp
was always on par with the slowest variant. It is clear, however, that such a function cannot be optimized for the specific case of an in-place operation, and inlining doesn't help here as it doesn't change the used instructions.
#include <stdio.h>
#include <stdlib.h>
#define MY_MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MY_MIN(a, b) (((a) < (b)) ? (a) : (b))
typedef float TYPE;
int main()
{
const TYPE a = (TYPE) (rand() % 100);
const TYPE b = a + (TYPE) (1 + rand() % 100);
double sum = 0.0;
for (unsigned int i = 0; i < 100000000; i++) {
TYPE x = (TYPE) (rand() % 100);
#if 1
if (x < a) x = a;
if (x > b) x = b;
#elif 0
if (x < a) x = a;
else if (x > b) x = b;
#elif 0
x = MY_MAX(a, MY_MIN(b, x));
#endif
sum += x;
}
printf("%lf\n", sum);
return 0;
}