Add int.__length__ method

There are algorithms that depend on the number of bits per digit be divisible by 5.

Also, the marshal format uses 15-bit digits. It is easy and efficient to split 30-bit digit on 15-bit digits.

1 Like

Which algorithms? And should that maybe be mentioned in longintrepr.h (which mentions marshal and a few other things)?

Just noting that gmpy2 (the popular Python wrapper for the GMP library) already supports len() for its integer type:

>>> import gmpy2
>>> for i in range(17):
...     print(i, len(gmpy2.mpz(i)))
0 1
1 1
2 2
3 2
4 3
5 3
6 3
7 3
8 4
9 4
10 4
11 4
12 4
13 4
14 4
15 4
16 5

So it’s the bit length. Although unlike the .bit_length() method, len() returns f for 0 instead of .bit_length()'s 0 result:

>>> z = gmpy2.mpz(0)
>>> len(z), z.bit_length()
(1, 0)
1 Like

gmpy2 also happens to have an “mpz.num_digits” function (with default decimal base)

>>> import gmpy2
>>> gmpy2.mpz(0xFFFF).num_digits()
5
>>> gmpy2.mpz(0xFFFF).num_digits(16)
4
1 Like

I would imagine this to be faster, though less readable:

import math

print(int(math.log10(abs(int(n)))) + 1)

If you read a few more messages, you get shown that this can have wrong results, platform dependent: Add int.__length__ method - #11 by mdickinson

1 Like

Potentially only your larger formula is wrong and one would have to think that through, so I prefer to say that math.log10(10**15) == math.log10(10**15 - 1), which shows that log10 itself already can’t distinguish these two numbers which have different lengths.

To avoid the floating-point imprecision of math.log10 and the sign inconsistency of str one can use decimal.Decimal instead:

import math
from decimal import Decimal

n = 10 ** 15 - 1
print(1 + math.floor(math.log10(n))) # outputs 16
print(len(str(n))) # outputs 15
print(len(Decimal(n).as_tuple().digits)) # outputs 15