Introdução
Frequentemente precisamos converter números de uma base para outra, seja para uma análise rápida, seja programando. O ambiente GNU/Linux oferece algumas ferramentas para trabalharmos com conversão de bases e serão elas que veremos nesse post.
Programas usados
Usarei os seguintes programas para exemplificar algumas mudanças de bases:
- bash
- printf
- bc
- dc
Convertendo bases numéricas usando o bash
O bash, através do operador $(()), consegue converter qualquer base entre 2 e 64 para a base decimal. A sintaxe é:
$ echo $(([IBASE]#[numero]))
Converter de hexadecimal para decimal
$ echo $((16#FFFFAAAA)) 4294945450
Converter de binário para decimal
$ echo $((2#1001111010)) 634
Veja que a saída é sempre na base decimal e isso limita bastante o uso desse operador.
Convertendo bases numéricas usando o comando printf
O comando printf é tanto um built-in de alguns shells quanto um comando externo. Se você estiver usando o bash então esse comando é integrado a ele e portanto será executado quando o comando printf for invocado. Em outros shells, que não possuem esse comando como built-in, o comando externo será usado.
Esse comando se assemelha muito com a função printf() encontrada na linguagem C, pois ambas as implementações utilizam strings de formato. A lista de formatos usada por printf é muito grande (veja uma em [1]) e somente os formatos dedicados à conversão de bases que serão usados aqui.
O modo básico de chamar o comando printf é:
$ printf [formato] [argumentos...]
Sendo que os formatos usados para converter números são:
+----------+----------------------------------------------------+ | Formato | Descricao | +----------+----------------------------------------------------+ | %x | converte o argumento para a base hexadecimal | +----------+----------------------------------------------------+ | %o | converte o argumento para a base octal | +----------+----------------------------------------------------+ | %d | converte o argumento para a base decimal com sinal | +----------+----------------------------------------------------+ | %u | converte o argumento para a base decimal sem sinal | +----------+----------------------------------------------------+
Os argumentos podem ser especificados em outras bases seguindo o formato aceito pelo bash.
+----------+-------------------------------+ | Formato | Descricao | +----------+-------------------------------+ | N | argumento na base decimal | +----------+-------------------------------+ | 0N | argumento na base octal | +----------+-------------------------------+ | 0[xX]N | argumento na base hexadecimal | +----------+-------------------------------+
Agora vamos converter alguns números usando printf:
Converter de decimal para hexadecimal
$ printf "%x\n" 123456 1e240 $ printf "%#x\n" 123456 0x1e140 $ printf "%x" -1 ffffffffffffffff
Pelo último exemplo, parece que minha versão do printf está trabalhando com números de 64 bits (meu sistema é 32). Veja também que isso é uma limitação, então tome cuidado para que o número de entrada não seja grande o suficiente para causar um overflow.
Converter de hexadecimal para decimal
$ printf "%d" 0x1e240 123456
Os exemplos são muitos, veja o link [1] para mais formatos e modificadores de printf. Veja que a conversão só ocorre entre as bases: decimal, hexadecimal e octal. O formato deve indicar a base de saída enquanto o argumento indica a base de entrada.
Convertendo bases numéricas usando o comando bc
O bc é um calculadora de mesa muito usada em scripts, principalmente para cálculos envolvendo números de ponto flutuante. Como era de se notar, o bc também pode ser usado para converter bases numéricas.
A sintaxe para o bc (usando pipes) é:
$ echo "obase=[OBASE]; ibase=[IBASE]; [numero]" | bc
Sendo que:
obase=[OBASE] : Define a base de saída. ibase=[IBASE] : Define a base de entrada. [numero] : O número a ser convertido na base [IBASE].
Exemplos:
Converter de decimal para hexadecimal
$ echo 'obase=16; ibase=10; 27878178171781787171114178178280' | bc 15FDF407F3B30687308F0FDECE8
Lembre-se que o bc é uma calculadora de precisão arbitrária, então o número de entrada pode ser tão grande quanto sua memória principal suportar (portanto sem overflows aqui).
Converter de hexadecimal para binário
$ echo 'obase=2; ibase=16; 1337AAABBBCCCDDD' | bc 1001100110111101010101010101110111011110011001100110111011101
Detalhe: Para bases de entrada maiores que 10, as letras devem vir em maiúsculas.
Converter de decimal para binário
$ echo 'obase=2; ibase=10; 88178184545178392278178178227827878' | bc 10000111110111000010100101000111000011110010100000011001110110001001\ 0000010100011110001100110111010001111010010100110
Claramente você pode variar obase e ibase para obter conversões diferentes. Veja que o bc fornece uma flexibilidade maior em relação a printf, pois ele pode trabalhar com qualquer base e não somente com três. Para finalizar essa parte gostaria de comentar que inverter a ordem de obase e ibase pode causar problemas. Por padrão o bc lê os números na base 10, mas quando usamos o ibase, a base padrão de leitura é alterada e com isso o número fornecido em obase não estará mais na base 10 (exceto para ibase=10). Para entender isso, veja os exemplos:
$ echo 'ibase=8; obase=16; 456' | bc
Irá converter 456 na base 8 para a base 16? Certo? Negativo. Como a base de leitura foi mudada para 8, então todos os números subsequentes serão tratados nessa base. Como o 16 na base 8 é 14, o comando acima irá realizar o mesmo que:
$ echo 'obase=14; ibase=8; 456' | bc
Então a lição é: Sempre use obase antes de ibase para evitar problemas.
Covertendo bases numéricas usando o comando dc
O dc também é uma calculadora de mesa com precisão arbitrária que nem o bc. A diferença entre as duas é que o dc usa a notação polonesa reversa para realizar os cálculos. Isso quer dizer que uma simples conta no bc:
$ echo '1 + 2*8 + 9/3' | bc
vira esse monstrinho no dc:
$ echo '1 2 8 * + 9 3 / + p' | dc
Para saber mais sobre o dc veja o link [2]. O que nos interessa aqui é como usar o dc para converter bases.
A sintaxe é:
$ echo '[OBASE]o[IBASE]i[NUMERO]p' | dc
Os comandos ‘o’ e ‘i’ definem as bases de saída e entrada respectivamente. Não esqueça do comando ‘p’ no final que serve para mostrar o resultado.
Veja alguns exemplos:
Converter de decimal para hexadecimal
$ echo '16o9887187151817817871004887428991271822783p' | dc 1D0E4AFAEAC2CF9EE1A63124274513E5BF
Converter de hexadecimal para binário
$ echo '2o16i898171A817817F817817B187181788C117AAAAp' | dc 100010011000000101110001101010000001011110000001011111111000000101111\ 000000101111011000110000111000110000001011110001000110000010001011110\ 10101010101010
Detalhe: Para bases de entrada maiores que 10, as letras devem vir em maiúsculas.
Números negativos no dc devem ser prefixados por ‘_’ e não pelo sinal de menos (já que ele é usado como operador de subtração). Então, para converter números negativos use o ‘_’ como sinal:
$ echo '2o_666p' | dc # converte -666 para a base 2 -1010011010 # <-- sem complemento de dois, infelizmente.
E por fim, basta lembrar também que a regra da ordem das bases no bc também vale para o dc, então sempre use ‘o’ antes de ‘i’ para evitar problemas.
Qual programa usar para conversão de bases?
Diante de tantas possibilidades sempre vem a cabeça sobre qual método utilizar. Eu pessoalmente acho que a ordem ideal deve ser essa:
1) Se for para converter qualquer base para decimal, use o operador $(()) do bash, pois ele tende a ser mais rápido que qualquer outro método.
2) Se a conversão ficar entre as bases dec/hex/oct então use printf. Se em seu sistema esse comando for built-in então ele apresentará maior velocidade em comparação ao bc/dc [3].
3) Para conversão qualquer base para qualquer base use o bc ou dc. Em alguns casos eu prefiro o dc porque ele utiliza menos caracteres para informar as bases (‘i’ para base de entrada enquanto “ibase” no bc). Por outro lado, levando em conta que o bc é a calculadora mais popular do linux, prefiro utilizá-lo em scripts públicos porque as pessoas estão mais acostumadas com sua sintaxe.
Conclusão
Conversão de bases numéricas é muito frequente quando estamos programando em shell script. A finalidade desse texto foi apresentar algumas formas de fazer tal tarefa usando programas simples e comuns em todas distribuições GNU/Linux.
Referências
[1] The printf command (Acessado em: Setembro/2011)
http://wiki.bash-hackers.org/commands/builtin/printf
[2] dc (computer program) (Acessado em: Setembro/2011)
http://en.wikipedia.org/wiki/Dc_%28computer_program%29
[3] Bash numerical base conversion (Acessado em: Setembro/2011)
http://www.issociate.de/board/post/353146/Bash_numerical_base_conversion.html
[4] Linux / UNIX: Convert Hexadecimal to Decimal Number (Acessado em: Setembro/2011)
http://www.cyberciti.biz/faq/linux-unix-convert-hex-to-decimal-number/
Pingback: Bit Hacking – Manipulação de Bits | Daemonio Labs
Pingback: Conversão de base numérica | Daemonio Labs