/**
 * Converte um valor hexadecimal para decimal com complemento de dois para numeros negativos
 * @param string $hex Valor hexadecimal
 * @return string Valor decimal na forma de string
 */
function converter_hex_dec($hex) {
    if (strlen($hex) % 2) {
        $hex = '0'.$hex;
    }

    // Obter os bytes na forma de vetor de inteiros
    // e usar o byte menos significativo (mais a direita)
    // simulando um tipo char da linguagem C
    $len = strlen($hex);
    $size = $len / 2;
    $bytes = array();
    for ($i = 0; $i < $len; $i += 2) {
        $hex_byte = substr($hex, $i, 2);
        $byte = hexdec($hex_byte);
        $bytes[] = (int)$byte;
    }

    // Converter vetor de bytes para inteiro
    $dec = converter_bytes_dec($bytes);

    return $dec;
}


/**
 * Obtem um numero decimal de um vetor de bytes
 * @param array[int] $bytes Vetor de bytes representados por inteiros (byte menos significativo)
 * @return String Valor decimal representado por uma string
 */
function converter_bytes_dec($bytes) {

    // Numero negativo
    if ($bytes[0] & (1 << 7)) {

        // Complemento de 2 (inverter bits e somar 1)
        $bytes = complemento2($bytes);
        $sinal = '-';

    // Numero positivo
    } else {
        $sinal = '+';
    }

    $dec = '0';
    $bit = '0';
    for ($i = count($bytes) - 1; $i >= 0; --$i) {
        for ($j = 0; $j < 8; ++$j) {
            if ($bytes[$i] & (1 << $j)) {
                $dec = bcadd($dec, bcpow('2', $bit), 0);
            }
            $bit = bcadd($bit, '1');
        }
    }
    return $sinal.$dec;
}

/**
 * Calcula o complemento de dois de um vetor de bytes
 * @param array[int] $bytes Vetor de bytes representados por inteiros (byte menos significativo)
 * @return array[int] Vetor de bytes convertidos
 */
function complemento2($bytes) {

    // Inverter bits
    $size = count($bytes);
    for ($i = $size - 1; $i >= 0; --$i) {
        $bytes[$i] = inverter_bits($bytes[$i]);
    }

    // Somar 1
    for ($i = $size - 1; $i >= 0; --$i) {
        for ($j = 0; $j < 8; ++$j) {
            $bytes[$i] = $bytes[$i] ^ (1 << $j);
            if ($bytes[$i] & (1 << $j)) {
                break 2;
            }
        }
    }

    return $bytes;
}

/**
 * Inverte os bits de determinado byte
 * @param int $byte Byte representado por um inteiro (byte menos significativo)
 * @return int Byte com os bits invertidos (byte menos significativo)
 */
function inverter_bits($byte) {
    for ($i = 7; $i >= 0; --$i) {
        $byte = $byte ^ (1 << $i);
    }
    return $byte;
}