Entendeu "ibase" e "obase" em caso de conversões com bc?

22

Costumo usar o bcutilitário para converter hexadecimal para decimal e vice-versa. No entanto, é sempre um pouco de tentativa e erro como ibasee obasedeve ser configurado. 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( Ano meu terceiro exemplo) precisa estar na mesma base que o valor que é convertido ( C0nos meus exemplos) e ibase( 16no meu terceiro exemplo) deve estar na base para a qual estou convertendo?

Martin
fonte
1
para cálculos hexadecimais (entrada e saída em hexadecimal) eu tenho que definir obase antes do ibase!
Paschalis

Respostas:

36

O que você realmente quer dizer é:

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

para hexadecimal e decimal e:

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

para decimal para hexadecimal.

Você não precisa fornecer os dois ibasee obasepara qualquer conversão envolvendo números decimais, pois essas configurações são 10.

Você não precisa dar tanto para as conversões, tais como binary-to-hex. Nesse caso, acho mais fácil entender as coisas se você der obaseprimeiro:

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

Se você fornecer ibaseprimeiro, ele altera a interpretação da seguinte obaseconfiguração, para que o comando tenha que ser:

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

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


Agora vamos descobrir por que seus três exemplos se comportam como eles.

  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, pois um valor de um dígito é interpretado em hexadecimal, de acordo com o POSIX . Isso pede bcpara lhe dizer o que C0₁₅ está na base A₁₅ = 10 e está respondendo corretamente a 180₁₀, embora essa certamente não seja a pergunta que você pretendia fazer.

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

    C0

    Esta é uma conversão nula na base 15.

    Por quê? Primeiro, porque o Fdígito único é interpretado em hexadecimal, como apontei no exemplo anterior. Mas agora que você definiu como base 15, a seguinte configuração da base de saída é interpretada dessa maneira e 10₁₅ = 15, para que você tenha 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 em F0vez de C0. Como não há Fdígito na base 15, bcprenda-o E0e dê E0como saída.

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

    192

    Este é o único de 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 pesquisar na especificação POSIX para entender por que Aé interpretado como hexadecimal 10 neste caso. O único problema é que é redundante definir a base de saída como A₁₆ = 10, já que esse é o valor padrão.

Warren Young
fonte
7

Configuração ibasesignifica que você precisa definir obasena mesma base. Explicar seus exemplos mostrará isso:

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

Você define os bcnú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 base 15: C = 12. 12 * 15 = 180.


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

Neste, você define 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 na 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 bchá uma exceção irônica: ibase=Ae obase=Asempre define entrada e saída como base 10. Na bcpágina de manual:

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

Esse comportamento é consagrado na especificação de bc: Na especificação do OpenGroup 2004bc :

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 ibase=Fconfiguração mudou sua base de entrada para a base 15 e por que eu recomendei sempre definir a base usando a base 10. Evite se confundir.

Bruce Ediger
fonte
@ StéphaneChazelas - Tenho uma lembrança de "ibase = A" trabalhando em uma máquina SysVr3 em 1989 ou mais. Aposto que remonta mais longe que Single Unix Spec. Eu não conseguia pesquisar no google rapidamente uma referência anterior.
precisa
Eu acho que é porque existem mais links para as especificações mais antigas, já que elas existem há mais tempo. O mesmo tipo de coisa acontece para a documentação do apache / mysql / bugzilla ... onde o google fornece o documento para as versões mais antigas primeiro, em vez da mais recente.
Stéphane Chazelas
5

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

No manual GNU bc :

Os números de um dígito sempre têm o valor do dígito, independentemente do valor do ibase . (ou seja, A = 10.) Para números de vários dígitos, bcaltere todos os dígitos de entrada maiores ou iguais a ibase para o valor de ibase -1. Isso faz com que o número FFFseja sempre o maior número de 3 dígitos da base de entrada.

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

A partir da especificação SUS no bc :

Quando ibase ou obase recebe um valor de um dígito da lista nas Convenções Lexicais em bc, o valor deve ser assumido em hexadecimal. (Por exemplo, ibase = A define para a base dez, independentemente do valor atual do ibase .) Caso contrário, o comportamento será indefinido quando dígitos maiores ou iguais ao valor do ibase aparecerem na entrada. Tanto a ibase quanto a obase devem ter valores iniciais de 10.

O principal fator que está faltando é que F não é de fato dezesseis, mas na verdade é quinze; portanto, quando você está configurando ibase = F, está definindo a base de entrada para quinze.

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

Random832
fonte
+1: truque bonito com ibase=A; ibase=16.
Warren Young
Isso é SUSv3, não SUSv6. SUSv4 está em pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html
Stéphane Chazelas
Eu sempre pensei que os 6 e 7 nos cabeçalhos eram a versão. Nunca vi nada de diferente - o que é o problema nº 1-5?
usar o seguinte código
@ Random832: SUS e POSIX não são a mesma coisa .
Warren Young
@WarrenYoung O SUS não incorpora o POSIX? Este parágrafo não possui tag de extensão e o documento diz coisas como "parte deste volume do POSIX.1-2008".
usar o seguinte comando
0

É sempre recomendável definir ibasee obaseusar um número de um dígito, em vez de um número como 16, pois, de acordo com a bcpágina de manual,

Os números de um dígito sempre têm o valor do dígito, independentemente do valor do ibase.

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

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

em vez de escrever echo "ibase=16; obase=A; C0" | bcpara especificar que a base de entrada é 16e a base de saída é 10. Ou, por exemplo, se você quer ter ambos ibasee obaseter 16 anos, é melhor usar

ibase=F+1; obase=F+1

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

ibase=E; obase=F+1

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

A diferença entre as duas formas se torna mais aparente, especialmente quando você está no ambiente de execução bcou escreve seus cálculos em um arquivo e depois passa esse arquivo bccomo argumento. Em tais situações, você pode ter que alterar os valores de ibasee obasevárias vezes, e usando a última forma, pode levar a sérias confusões e erros. (experencie)

Hedayat Mahdipour
fonte