-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
py/objint.c: Add support for int.bit_length(). #11679
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
0811b35
b71f370
1747ae9
9dd4f8f
d6ee656
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1390,6 +1390,24 @@ void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t | |
mpz_free(n); | ||
} | ||
|
||
#if MICROPY_INT_BIT_LENGTH | ||
mp_uint_t mpz_bit_length_inpl(mpz_t *n) { | ||
if (n->len == 0) { | ||
return 0; | ||
} | ||
|
||
mpz_t *dest = mpz_clone(n); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of copying the mpz into a new integer and modifying it, you can just count the digits in the mpz. |
||
mp_uint_t num_bits = 0; | ||
while (dest->len > 0) { | ||
dest->len = mpn_shr(dest->dig, dest->dig, dest->len, 1); | ||
num_bits++; | ||
} | ||
|
||
mpz_free(dest); | ||
return num_bits; | ||
} | ||
#endif | ||
|
||
#if 0 | ||
these functions are unused | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -450,9 +450,30 @@ STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { | |
} | ||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 3, 4, int_to_bytes); | ||
|
||
#if MICROPY_INT_BIT_LENGTH | ||
STATIC mp_obj_t int_bit_length(size_t n_args, const mp_obj_t *args) { | ||
(void)n_args; | ||
#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This unnecessarily creates an mpz if the input is a small int. Instead, these functions should handle small ints separately, and only send them off to mpz if necessary. |
||
return mp_obj_int_mpz_bit_length(args[0]); | ||
#else | ||
mp_uint_t dest = MP_OBJ_SMALL_INT_VALUE(args[0]); | ||
mp_uint_t num_bits = 0; | ||
while (dest > 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not work for negative numbers. It will always return 32 (or 64 depending on The reason the tests pass with negative numbers is that this code is never hit. |
||
dest >>= 1; | ||
num_bits++; | ||
} | ||
return mp_obj_new_int_from_uint(num_bits); | ||
#endif | ||
} | ||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_bit_length_obj, 0, 1, int_bit_length); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We also need to provide this for the "long long" implementation. |
||
#endif | ||
|
||
STATIC const mp_rom_map_elem_t int_locals_dict_table[] = { | ||
{ MP_ROM_QSTR(MP_QSTR_from_bytes), MP_ROM_PTR(&int_from_bytes_obj) }, | ||
{ MP_ROM_QSTR(MP_QSTR_to_bytes), MP_ROM_PTR(&int_to_bytes_obj) }, | ||
#if MICROPY_INT_BIT_LENGTH | ||
{ MP_ROM_QSTR(MP_QSTR_bit_length), MP_ROM_PTR(&int_bit_length_obj) }, | ||
#endif | ||
}; | ||
|
||
STATIC MP_DEFINE_CONST_DICT(int_locals_dict, int_locals_dict_table); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -334,7 +334,7 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i | |
} | ||
} | ||
|
||
#if MICROPY_PY_BUILTINS_POW3 | ||
#if (MICROPY_PY_BUILTINS_POW3 || MICROPY_INT_BIT_LENGTH) | ||
STATIC mpz_t *mp_mpz_for_int(mp_obj_t arg, mpz_t *temp) { | ||
if (mp_obj_is_small_int(arg)) { | ||
mpz_init_from_int(temp, MP_OBJ_SMALL_INT_VALUE(arg)); | ||
|
@@ -344,7 +344,9 @@ STATIC mpz_t *mp_mpz_for_int(mp_obj_t arg, mpz_t *temp) { | |
return &(arp_p->mpz); | ||
} | ||
} | ||
#endif | ||
|
||
#if MICROPY_PY_BUILTINS_POW3 | ||
mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { | ||
if (!mp_obj_is_int(base) || !mp_obj_is_int(exponent) || !mp_obj_is_int(modulus)) { | ||
mp_raise_TypeError(MP_ERROR_TEXT("pow() with 3 arguments requires integers")); | ||
|
@@ -373,6 +375,18 @@ mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { | |
} | ||
#endif | ||
|
||
#if MICROPY_INT_BIT_LENGTH | ||
mp_obj_t mp_obj_int_mpz_bit_length(mp_obj_t size) { | ||
mpz_t n_temp; | ||
mpz_t *n = mp_mpz_for_int(size, &n_temp); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above, if you handle small ints directly, you don't need to allocate an mpz here. |
||
mp_uint_t res = mpz_bit_length_inpl(n); | ||
if (n == &n_temp) { | ||
mpz_deinit(n); | ||
} | ||
return mp_obj_new_int_from_ull(res); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. res will always fit in a small int, so can just use MP_OBJ_NEW_SMALL_INT directly. |
||
} | ||
#endif | ||
|
||
mp_obj_t mp_obj_new_int(mp_int_t value) { | ||
if (MP_SMALL_INT_FITS(value)) { | ||
return MP_OBJ_NEW_SMALL_INT(value); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# tests int.bit_length | ||
|
||
try: | ||
int.bit_length | ||
except AttributeError: | ||
print('SKIP') | ||
raise SystemExit | ||
|
||
n = -37 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs tests for big ints (i.e. mpz). Also some more interesting bit patterns around the boundaries. |
||
print(n.bit_length()) | ||
|
||
n = 1024 | ||
print(n.bit_length()) | ||
|
||
n = -1024 | ||
print(n.bit_length()) | ||
|
||
print((2048).bit_length()) | ||
|
||
print((-2048).bit_length()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be defined (and defaulted) in mpconfig.h.
In general rather than enabling things in coverage, just make them
MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING
in mpconfig, which will be enabled by coverage.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also for consistency I think this should be called
MICROPY_PY_BUILTINS_INT_BIT_LENGTH
.