Entender “ibase” e “obase” no caso de conversões com bc?

18

Costumo usar o utilitário bc para converter hexadecimal em decimal e vice-versa. No entanto, é sempre um pouco de tentativa e erro como ibase e obase devem ser configurados. Por exemplo, aqui eu quero converter o valor hexadecimal C0 em decimal:

$ echo "ibase=F;obase=A;C0" | bc
180
$ echo "ibase=F;obase=10;C0" | bc
C0
$ echo "ibase=16;obase=A;C0" | bc
192

Qual é a lógica aqui? obase ( A no meu terceiro exemplo) precisa estar na mesma base que o valor que é convertido ( C0 em meus exemplos) e ibase ( 16 em meu terceiro exemplo) tem que estar em a base para a qual estou me convertendo?

    
por Martin 30.04.2015 / 14:48

4 respostas

28

O que você realmente quer dizer é:

$ echo "ibase=16; C0" | bc
192

para hexadecimal e:

$ echo "obase=16; 192" | bc
C0

para decimal para hexadecimal.

Você não precisa fornecer ibase e obase para qualquer conversão envolvendo números decimais, já que essas configurações são padronizadas para 10.

Você do precisa fornecer ambos para conversões como binário para hexadecimal. Nesse caso, acho mais fácil dar sentido às coisas se você der obase primeiro:

$ echo "obase=16; ibase=2; 11000000" | bc
C0

Se você der ibase primeiro, ele mudará a interpretação da seguinte configuração de obase , de modo que o comando tenha que ser:

$ echo "ibase=2; obase=10000; 11000000" | bc
C0

Isso ocorre porque, nessa ordem, o valor obase é interpretado como um número binário, portanto, é necessário fornecer 10000₂ = 16 para obter a saída em hexadecimal. Isso é desajeitado.

Agora, deixe-me explicar por que seus três exemplos se comportam como fazem.

  1. echo "ibase=F;obase=A;C0" | bc

    180

    Isso define a base de entrada como 15 e a base de saída como 10, já que um valor de dígito único é interpretado em hexadecimal, de acordo com o POSIX . Isso pede a bc para lhe dizer o que C0₁₅ está na base A₁₅ = 10, e está respondendo corretamente 180₁₀, embora essa certamente não seja a pergunta que você queria fazer.

  2. echo "ibase=F;obase=10;C0" | bc

    C0

    Esta é uma conversão nula na base 15.

    Por quê? Primeiro, porque o dígito F único é interpretado em hexadecimal, como apontei no exemplo anterior. Mas agora que você configurou para a base 15, a seguinte configuração da base de saída é interpretada dessa forma, e 10₁₅ = 15, então você tem uma conversão nula de C0₁₅ para C0₁₅.

    É isso mesmo, a saída não está em hexadecimal como você estava assumindo, está na base 15!

    Você pode provar isso para si mesmo tentando converter F0 em vez de C0 . Como não há F dígito na base 15, bc bloqueia a E0 e fornece E0 como saída.

  3. echo "ibase=16; obase=A; C0"

    192

    Este é o único dos seus três exemplos que provavelmente tem algum uso prático.

    Ele está mudando a base de entrada para hexadecimal primeiro , para que você não precise mais cavar na especificação POSIX para entender porque A é interpretado como hex, 10 neste caso. O único problema é que é redundante definir a base de saída como A₁₆ = 10, já que é o valor padrão.

por 30.04.2015 / 15:07
6

Definir ibase significa que você precisa definir obase nessa mesma base. Explicar seus exemplos mostrará isso:

echo "ibase=F;obase=A;C0" | bc

Você define bc para considerar números de entrada como representados na base 15 com o "ibase = F". "obase = A" define os números de saída para a base 10, que é o padrão.

bc lê C0 como um número de base 15: C = 12. 12 * 15 = 180.

echo "ibase=F;obase=10;C0" | bc

Neste, você ajusta a entrada para a base 15 e a saída para 10 - na base 15, então a base de saída é 15. A entrada C0 na base 15 é a saída C0 na base 15.

echo "ibase=16;obase=A;C0" | bc

Defina a entrada para a base 16, a saída para a base 10 (A na base 16 é 10 na base 10).

C0 convertido em base 10 é: 12 * 16 = 192

Minha regra pessoal é definir obase primeiro, para que eu possa usar a base 10. Em seguida, defina ibase, também usando a base 10.

Observe que bc tem uma exceção irônica: ibase=A e obase=A sempre definem entrada e saída como base 10. Na página bc man:

Single digit numbers always have the value of the digit 
regardless of the value of ibase.

Esse comportamento está consagrado na especificação de bc : da 2004 OpenGroup bc specification :

When either ibase or obase is assigned a single digit value from 
the list in 'Lexical Conventions in bc', the value shall be assumed
in hexadecimal. (For example, ibase=A sets to base ten, regardless 
of the current ibase value.) Otherwise, the behavior is undefined 
when digits greater than or equal to the value of ibase appear in
the input.

É por isso que a configuração ibase=F alterou sua base de entrada para a base 15 e por que eu recomendei sempre definir a base usando a base 10. Evite confundir-se.

    
por 30.04.2015 / 15:14
4

Todos os números são interpretados pelo GNU bc como a base de entrada atual que está em vigor para a instrução na qual o número aparece. Quando você usa um dígito fora da entrada atual, interprete-os como o dígito mais alto disponível na base (9 em ) quando parte de um número de múltiplos dígitos, ou como seus valores normais quando usados como um número de dígito único ( A == 10 em decimal).

No manual do GNU bc :

Single digit numbers always have the value of the digit regardless of the value of ibase. (i.e. A = 10.) For multi-digit numbers, bc changes all input digits greater or equal to ibase to the value of ibase-1. This makes the number FFF always be the largest 3 digit number of the input base.

No entanto, você deve estar ciente de que o padrão POSIX só define esse comportamento para atribuições a ibase e obase , e não em qualquer outro contexto.

Da especificação do SUS em bc :

When either ibase or obase is assigned a single digit value from the list in Lexical Conventions in bc , the value shall be assumed in hexadecimal. (For example, ibase=A sets to base ten, regardless of the current ibase value.) Otherwise, the behavior is undefined when digits greater than or equal to the value of ibase appear in the input. Both ibase and obase shall have initial values of 10.

O fator chave que você está perdendo é que F não é de fato dezesseis, mas na verdade é quinze, então quando você está ajustando ibase = F você está ajustando a base de entrada para quinze.

Portanto, para portar o ibase para hexadecimal de um estado desconhecido, você precisa usar duas instruções: ibase=A; ibase=16 . No entanto, no início do programa, você pode confiar que é decimal e simplesmente usar ibase=16 .

    
por 30.04.2015 / 18:11
0

É sempre recomendável definir ibase e obase usando um número de um único dígito, em vez de um número como 16 , já que de acordo com bc man page,

Single digit numbers always have the value of the digit regardless of the value of ibase.

Isso significa que A,B,...,F sempre tem os valores 10,11,...,15 , respectivamente, independentemente do valor de ibase . Você também pode usar F+1 para especificar o número 16 . Por exemplo, é melhor você escrever

echo "ibase=F+1; obase=A; C0" | bc

em vez de escrever echo "ibase=16; obase=A; C0" | bc para especificar que a base de entrada é 16 e a base de saída é 10 . Ou, por exemplo, se você quiser que os dois ibase e obase sejam 16, é melhor usar

ibase=F+1; obase=F+1

em vez de usar ibase=16; obase=10 . Da mesma forma, se você for inserir seus números na base 14 e gerá-los na base 16, use

ibase=E; obase=F+1

Embora as formas de banho tenham os mesmos resultados, a primeira é menos propensa a erros, enquanto a segunda pode levar a mais confusão e erro.

A diferença entre os dois formulários torna-se especialmente mais evidente quando você está no ambiente de execução de bc , ou escreve seus cálculos em um arquivo e passa esse arquivo para bc como um argumento . Em tais situações, talvez você precise alterar os valores de ibase e obase várias vezes e, usando o último formulário, pode levar a sérios confusões e erros. (experimente)

    
por 31.01.2018 / 19:13

Tags