I have a contract that makes use of cartesian coordinates. My initial thought is to map a single location (x,y) to a value/struct. I would like to define any given pair of coordinates by a single byte32 that can be converted back into coordinates. Ex:

int16 x, int16 y;
byte32 key = coordinatesToBytes32(x, y);

and vise-versa:

[x, y] = bytes32ToCoordinates(key);

After jumping into solc@^0.5.0 I discovered you can no longer explicitly convert between these types which made it difficult for me to cast, mask, shift, and run bitwise operations. For reference, here is where I made it to (note the solidity version):

pragma solidity ^0.4.17;

contract CoordinateUtils {
    function coordinatesToBytes32(int16 x, int16 y) internal pure returns(bytes32) {
        return (bytes32(x) << 16 & 0xFFFFFFFF) | bytes16(y);  // left shift extends negative so we mask out
    function bytes32ToCoordinates(bytes32 b) internal pure returns(int16 x, int16 y) {
        return (int16(b >> 16), int16(b));

Also, I am open to other suggestions on the storage of cartesian related values if this seems inefficient.


First of all, int16 is just 2 bytes wide (16 bit), not 16 bytes. Did you mean int128?

Second, you still may pack/unpack two int16 values into single bytes32 in Solidity 0.5.x, you just need a bit more type casts:

function pack (int16 a, int16 b) public pure returns (bytes32) {
  return bytes32 (bytes2 (a)) >> 16 | bytes2 (b);

function unpack (bytes32 x) public pure returns (int16, int16) {
  return (int16 (bytes2 (x << 16)), int16 (bytes2 (x)));

Note, the direction of shifts. This is because, for bytesN widening works differently than for numbers:

bytes2 a = 0x0102;
bytes4 b = bytes32 (a); // 0x01020000 (padded from right)

int16 c = 0x0102;
int32 d = int32 (c); // 0x00000102 (padded from left)
| improve this answer | |
  • Perfect! Thank you for correcting me on the int16 byte length. So in this scenario it would make more sense to pack two int128 into a bytes32, or two int16 into a bytes4 correct? Also good to know about the different padding behaviors, I can see why they made this change that @markus-waas pointed out. – Neil Harlow Jul 2 '19 at 18:03

The behavior changed in Solidity 0.5.x.

Conversions between bytesX and uintY of different size are now disallowed due to bytesX padding on the right and uintY padding on the left which may cause unexpected conversion results. The size must now be adjusted within the type before the conversion. For example, you can convert a bytes4 (4 bytes) to a uint64 (8 bytes) by first converting the bytes4 variable to bytes8 and then to uint64. You get the opposite padding when converting through uint32.


So you can convert an int16 to bytes32 like this:

int16 x = 5;
bytes32 y = bytes32(int256(x));

Ensure that this is your intended behavior according to the padding ('bytesX padding on the right and uintY padding on the left').

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.