Skip to content

Commit 281009c

Browse files
committed
bpo-29782: Consolidate _Py_Bit_Length()
In pythonGH-2866, _Py_Bit_Length() was added to pymath.h for lack of a better location. pythonGH-20518 added a more appropriate header file for bit utilities. It also shows how to properly use intrinsics. This allows reconsidering bpo-29782. * Move the function to the new header. * Changed return type to match __builtin_clzl and reviewed usage. * Use intrinsics where available. * Pick a (mostly theoretical) fallback implementation suitable for inlining.
1 parent 3ab3475 commit 281009c

File tree

4 files changed

+29
-27
lines changed

4 files changed

+29
-27
lines changed

Include/internal/pycore_bitutils.h

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
- _Py_bswap64(uint64_t)
88
*/
99

10-
#ifndef Py_INTERNAL_BSWAP_H
11-
#define Py_INTERNAL_BSWAP_H
10+
#ifndef Py_INTERNAL_BITUTILS_H
11+
#define Py_INTERNAL_BITUTILS_H
1212
#ifdef __cplusplus
1313
extern "C" {
1414
#endif
@@ -131,8 +131,32 @@ _Py_popcount32(uint32_t x)
131131
}
132132

133133

134+
// Return the index of the most significant 1 bit in 'x'. This is the smallest
135+
// integer k such that x < 2**k. Equivalent to floor(log2(x)) + 1 for x != 0.
136+
static inline int
137+
_Py_bit_length(unsigned long x) {
138+
if (!x) {
139+
return 0;
140+
}
141+
#if (defined(__clang__) || defined(__GNUC__))
142+
return sizeof(unsigned long) * 8 - __builtin_clzl(x);
143+
#elif defined(_MSC_VER)
144+
Py_BUILD_ASSERT(4 == sizeof(unsigned long));
145+
unsigned long msb;
146+
_BitScanReverse(&msb, x);
147+
return 32 - msb;
148+
#else
149+
int msb = 0;
150+
while (x != 0) {
151+
msb += 1;
152+
x >>= 1;
153+
}
154+
return msb;
155+
#endif
156+
}
157+
158+
134159
#ifdef __cplusplus
135160
}
136161
#endif
137-
#endif /* !Py_INTERNAL_BSWAP_H */
138-
162+
#endif /* !Py_INTERNAL_BITUTILS_H */

Include/pymath.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,4 @@ PyAPI_FUNC(void) _Py_set_387controlword(unsigned short);
227227
* behavior. */
228228
#define _Py_InIntegralTypeRange(type, v) (_Py_IntegralTypeMin(type) <= v && v <= _Py_IntegralTypeMax(type))
229229

230-
/* Return the smallest integer k such that n < 2**k, or 0 if n == 0.
231-
* Equivalent to floor(log2(x))+1. Also equivalent to: bitwidth_of_type -
232-
* count_leading_zero_bits(x)
233-
*/
234-
#ifndef Py_LIMITED_API
235-
PyAPI_FUNC(unsigned int) _Py_bit_length(unsigned long d);
236-
#endif
237-
238230
#endif /* Py_PYMATH_H */

Modules/mathmodule.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ raised for division by zero and mod by zero.
5353
*/
5454

5555
#include "Python.h"
56+
#include "pycore_bitutils.h"
5657
#include "pycore_dtoa.h"
5758
#include "_math.h"
5859

Python/pymath.c

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,3 @@ round(double x)
7979
return copysign(y, x);
8080
}
8181
#endif /* HAVE_ROUND */
82-
83-
static const unsigned int BitLengthTable[32] = {
84-
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
85-
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
86-
};
87-
88-
unsigned int _Py_bit_length(unsigned long d) {
89-
unsigned int d_bits = 0;
90-
while (d >= 32) {
91-
d_bits += 6;
92-
d >>= 6;
93-
}
94-
d_bits += BitLengthTable[d];
95-
return d_bits;
96-
}

0 commit comments

Comments
 (0)