For C language, the stdc_bit_floor()
series of APIs would be introduced in the C23 standard. There are a type-generic version (stdc_bit_floor()
macro) and type-specific versions (stdc_bit_floor_*()
) defined in the header <stdbit.h>
. In the near future this will be the standard way to round integers to a power of 2.
The latest draft of the C23 standard (N3096)
For C++, C++20 has std::bit_floor which does the exact same thing.
Both stdc_bit_floor(x)
and std::bit_floor(x)
are required to return 0
if the input value x
is zero.
If your compiler and C library do not support C23 yet, here is an implementation that I consider it an "ultimate" solution. I intended the library writers to copy this code and adapt appropriately.
The following code
is targeted for C language (not C++),
uses compiler built-ins to yield efficient code (CLZ or BSR instruction) if compiler supports any,
is portable (standard C and no assembly) with the exception of built-ins, and
addresses undefined behavior of the compiler built-ins (when x
is zero).
#include <limits.h>
#ifdef _MSC_VER
# if _MSC_VER >= 1400
/* _BitScanReverse is introduced in Visual C++ 2005 and requires
<intrin.h> (also introduced in Visual C++ 2005). */
#include <intrin.h>
#pragma intrinsic(_BitScanReverse)
#pragma intrinsic(_BitScanReverse64)
# define HAVE_BITSCANREVERSE 1
# endif
#endif
/* Macro indicating that the compiler supports __builtin_clz().
The name HAVE_BUILTIN_CLZ seems to be the most common, but in some
projects HAVE__BUILTIN_CLZ is used instead. */
#ifdef __has_builtin
# if __has_builtin(__builtin_clz)
# define HAVE_BUILTIN_CLZ 1
# endif
#elif defined(__GNUC__)
# if (__GNUC__ > 3)
# define HAVE_BUILTIN_CLZ 1
# elif defined(__GNUC_MINOR__)
# if (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
# define HAVE_BUILTIN_CLZ 1
# endif
# endif
#endif
unsigned int stdc_bit_floor_ui(unsigned int x);
unsigned long stdc_bit_floor_ul(unsigned long x);
unsigned long long stdc_bit_floor_ull(unsigned long long x);
/**
* Returns the largest power of two that is not greater than x. If x
* is 0, returns 0.
*/
unsigned int stdc_bit_floor_ui(unsigned int x)
{
#ifdef HAVE_BITSCANREVERSE
if (x <= 0) {
return 0;
} else {
unsigned long int index;
(void) _BitScanReverse(&index, x);
return (1U << index);
}
#elif defined(HAVE_BUILTIN_CLZ)
if (x <= 0) {
return 0;
}
return (1U << (sizeof(x) * CHAR_BIT - 1 - __builtin_clz(x)));
#else
/* Fastest known solution without compiler built-ins or integer
logarithm instructions.
From the book "Hacker's Delight".
Converted to a loop for smaller code size.
("gcc -O3" will unroll this.) */
{
unsigned int shift;
for (shift = 1; shift < sizeof(x) * CHAR_BIT; shift <<= 1) {
x |= (x >> shift);
}
}
return (x - (x >> 1));
#endif
}
/**
* Returns the largest power of two that is not greater than x. If x
* is 0, returns 0.
*/
unsigned long stdc_bit_floor_ul(unsigned long x)
{
#ifdef HAVE_BITSCANREVERSE
if (x <= 0) {
return 0;
} else {
unsigned long index;
(void) _BitScanReverse(&index, x);
return (1UL << index);
}
#elif defined(HAVE_BUILTIN_CLZ)
if (x <= 0) {
return 0;
}
return (1UL << (sizeof(x) * CHAR_BIT - 1 - __builtin_clzl(x)));
#else
{
unsigned int shift;
for (shift = 1; shift < sizeof(x) * CHAR_BIT; shift <<= 1) {
x |= (x >> shift);
}
}
return (x - (x >> 1));
#endif
}
/**
* Returns the largest power of two that is not greater than x. If x
* is 0, returns 0.
*/
unsigned long long stdc_bit_floor_ull(unsigned long long x)
{
#if (defined(HAVE_BITSCANREVERSE) && \
ULLONG_MAX == 18446744073709551615ULL)
if (x <= 0) {
return 0;
} else {
/* assert(sizeof(__int64) == sizeof(long long)); */
unsigned long int index;
(void) _BitScanReverse64(&index, x);
return (1ULL << index);
}
#elif defined(HAVE_BUILTIN_CLZ)
if (x <= 0) {
return 0;
}
return (1ULL << (sizeof(x) * CHAR_BIT - 1 - __builtin_clzll(x)));
#else
{
unsigned int shift;
for (shift = 1; shift < sizeof(x) * CHAR_BIT; shift <<= 1) {
x |= (x >> shift);
}
}
return (x - (x >> 1));
#endif
}