I have an int. I want it represented as bytes — as many bytes as are needed to represent it. (Yes, the integer is nonnegative and I want an unsigned bytes representation.)
So far as I can see the only sensible way to achieve that right now is:
b = i.to_bytes( (i.bit_length()+7)//8 )
This feels surprisingly clunky for what I’d expect to be a relatively common operation. Have I overlooked an easier alternative?
And this example explicitly included in docs. Maybe it would be more obvious with something like div(i.bit_length(), 8, rounding='up') as length.
Anyway, “workaround” seems to be clear. Though, it will not work for twos complement:
>>> n = -129
>>> l = (n.bit_length()+7)//8; l
1
>>> n.to_bytes(l, signed=True)
Traceback (most recent call last):
File "<python-input-7>", line 1, in <module>
n.to_bytes(l, signed=True)
~~~~~~~~~~^^^^^^^^^^^^^^^^
OverflowError: int too big to convert
You have to decide: should length default be same when signed=True or not. So far your proposal is not complete.
Can you provide some justification for your expectations? Examples from popular projects, etc.
I would rather say, that more common are exports/imports from fixed-size integer formats.
Until v3.11 (see issue 89318), no defaults were provided for length/byteorder. Now we have default value for length. Changing that will introduce a backward incompatibility.
Just to check: you’re envisaging some new function, there, not talking about an existing one?
It feels like there are three points of friction here, and addressing any of them would improve things:
int.to_bytes() can’t give you as many bytes as needed
There is int.bit_length() but no int.byte_length()
There is no ceiling integer division function (i.e. your div(), above)
One possibility might be a special length value. Off the top of my head, -1 and None are plausible choices. Another would be a new function on int.
I did say, in my case, that “the integer is nonnegative and I want an unsigned bytes representation”. And I was asking a question rather than proposing a new feature. Notwithstanding that, I see two natural options:
Don’t support variable length for signed representations
For signed encodings, output sufficient bytes that there is at least one sign bit (if people want this, it would be good to provide an implementation as it looks like a major headache to get right unassisted)
(If some kind of int.to_var_bytes() function was provided it would be trivial to omit the signed argument.)
My use case happens to be encoding integers larger than 2**63 in an sqlite database as BLOBs. It could equally be useful for putting them in a websocket binary message, chunked data formats, etc.
Yes, we have no currently some methods for integer divisions with a different rounding (e.g. to nearest, round up, etc). But it’s a recurring proposal, see e.g. this thread.
My point is that such function could make obvious that ceiling rounding is happened here in integer division. That makes length calculation obvious too, isn’t?
One possibility might be a special length value.
And why this is better than an explicit length?
Don’t support variable length for signed representations
That will just complicate API for no good reasons.
My use case happens to be encoding integers larger than 2**63 in an sqlite database as BLOBs.
Methods on Python builtins are designed not just to fit your use case.
An interesting idea, though I worry about its efficiency. (I just tested and, though it’s hard to be precise, it seems to be about 10% slower for small numbers in small buffers, running up to 30-40% slower for large numbers in large buffers.
I guess you could employ some kind of doubling retry strategy to cope with arbitrarily large integers, but…
I’ve just noticed that PyLong_AsNativeBytes() was added to the C API in 3.13 , and seems to be a pretty thorough implementation that covers all the bases in this area.
It would form a lovely basis for both an API and implementation for a Python equivalent…