Is there an elegant way to cast a bigger datatype to a smaller one without causing the result to overflow?
E.g. casting 260
to uint8_t
should result in 255
instead of 4
.
A possible solution would be:
#include <limits.h>
#include <stdint.h>
inline static uint8_t convert_I32ToU8(int32_t i32)
{
if(i32 < 0) return 0;
if(i32 > UINT8_MAX) return UINT8_MAX;
return (uint8_t)i32;
}
Although this solution works, I wonder if there is a better way (without having to create lots of conversion functions).
Solution should be in C (with optionally GCC compiler extensions).
Since C11 you can use the new
_Generic
selection featureYou can remove the unnecessary types to make it shorter. After that just call it as
CLAMP(type, value)
like thisThis way you can clamp to almost any types, including floating-point types or
_Bool
if you add more types to the support list. Beware of the type width and signness issues when using itDemo on Godlbolt
You can also use the GNU
typeof
or__auto_type
extensions to make theCLAMP
macro cleaner and safer. These extensions also work in older C versions so you can use them in you don't have access to C11Another simple way to do this in older C versions is to specify the destination bitwidth
Beside the fact that it doesn't work for
long long
andunsigned long long
without some changes, this also implies the use of 2's complements. You can callCLAMP
with a negative bit width to indicate a signed type or callCLAMP_SIGN
/CLAMP_UNSIGN
directionAnother disadvantage is that it just clamps the values and doesn't cast to the expected type (but you can use
typeof
or__auto_type
as above to return the correct type)Demo on Godbolt