Solidity EVM – How to Convert Default Two’s Complement to Sign Magnitude

bit-manipulationevmintegerssolidity

According to this answer the EVM by default uses two's complement notation for handling signed integers. However for my application I would like to work with the sign-magnitude representation
where the sign can either be 0 or 1 corresponding to + or – respectively and the remaining bits comprise the magnitude. For example, if a int8 variable has a value of +1 it would be represented in binary as 0000_0001 and if it has a value of -1 it would 1000_0001 in sign-magnitude notation.

However since the EVM uses two's complement I first have to convert it into sign-magnitude representation before I can work with it. How exactly would I do this in a gas-efficient manner?

function twos_comp_to_sign_mag(int8) returns(int8);

// 255 in binary is 1111_1111 or -1 in two's complement notation
// 129 in binary is 1000_0001 or -1 in sign-magnitude notation
twos_comp_to_sign_mag(255) == 129

Best Answer

I asked basically the same question on StackOverflow but for python, below is my solidity adaptation using this answer:

// SPDX-License-Identifier: GPL-3.0
contract TwosComplement {

    function twos_comp_to_sign_mag(int8 value) external pure returns(int8) {
        
        int8 mask = 2**7 - 1; // 0111_1111

        if (value < 0) {
            value = -(value & mask);
        }
        return (-1 & value) | (value & mask);        
    }
}

where the following holds true:

twos_comp_to_sign_mag(positiveInt) == positiveInt;
// e.g.: twos_comp(0) == 0

twos_comp_to_sign_mag(negativeInt) == -128 - negativeInt;
// ex1: twos_comp(-1) == -128 - (-1) = -128 + 1 = -127
// ex2: twos_comp(-127) == -128 - (-127) = -128 + 127 = -1
// ex3: twos_comp(-128) == -128 - (-128) = -128 + 128 = 0

To adjust this for larger integer types is really simple; change the variable types accordingly and update the mask, so for int16 the mask will be 2**15 - 1.

Related Topic