Capire "ibase" e "obase" in caso di conversioni con bc?


22

Uso spesso l' bcutilità per convertire esadecimali in decimali e viceversa. Tuttavia, è sempre un po 'tentativo ed errore come ibasee obasedovrebbe essere configurato. Ad esempio qui voglio convertire il valore esadecimale C0 in decimale:

$ 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 è la logica qui? obase( Anel mio terzo esempio) deve trovarsi nella stessa base del valore che viene convertito ( C0nei miei esempi) e ibase( 16nel mio terzo esempio) deve trovarsi nella base in cui sto convertendo?


1
per i calcoli esadecimali (input e output in esadecimale) devo impostare obase prima di ibase!
Paschalis,

Risposte:


36

Quello che vuoi davvero dire è:

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

per esadecimali e:

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

per decimale-esadecimale.

Non è necessario fornire entrambi ibasee obaseper qualsiasi conversione che implichi numeri decimali, poiché queste impostazioni predefinite sono 10.

È Non c'è bisogno di dare sia per le conversioni, come binary-to-esadecimale. In tal caso, trovo più semplice dare un senso alle cose se dai per obaseprimo:

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

Se ibaseinvece dai prima, cambia l'interpretazione della seguente obaseimpostazione, quindi il comando deve essere:

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

Questo perché in questo ordine, il obasevalore viene interpretato come un numero binario, quindi è necessario fornire 10000₂ = 16 per ottenere l'output in esadecimale. È goffo.


Ora scopriamo perché i tuoi tre esempi si comportano come loro.

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

    180

    Ciò imposta la base di input su 15 e la base di output su 10, poiché un valore a una cifra viene interpretato in esadecimale, secondo POSIX . Questo ti chiede bcdi dire cosa C0₁₅ è nella base A₁₅ = 10, e sta rispondendo correttamente a 180₁₀, sebbene questa non sia certamente la domanda che intendevi porre.

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

    C0

    Questa è una conversione nulla nella base 15.

    Perché? Innanzitutto, poiché la singola Fcifra viene interpretata in esadecimale, come ho sottolineato nell'esempio precedente. Ma ora che hai impostato la base 15, la seguente impostazione della base di output viene interpretata in questo modo e 10₁₅ = 15, quindi hai una conversione nulla da C0₁₅ a C0₁₅.

    Esatto, l'output non è in esadecimale come si supponeva, è nella base 15!

    Puoi provare questo a te stesso cercando di convertire F0invece di C0. Poiché non vi è alcuna Fcifra nella base 15, la bcblocca su E0e fornisce E0come output.

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

    192

    Questo è l'unico dei tuoi tre esempi che probabilmente ha qualche utilità pratica.

    Sta cambiando la base di ingresso in esadecimale prima , in modo che non è più necessario scavare nel POSIX spec comprendere perché Aviene interpretato come esadecimale, 10 in questo caso. L'unico problema è che è ridondante impostare la base di output su A₁₆ = 10, poiché questo è il suo valore predefinito.


7

L'impostazione ibasesignifica che è necessario impostare la obasestessa base. Spiegare i tuoi esempi mostrerà questo:

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

Si imposta bcper considerare i numeri di input come rappresentato nella base 15 con "ibase = F". "obase = A" imposta i numeri di output su base 10, che è l'impostazione predefinita.

bc legge C0 come un numero di base 15: C = 12. 12 * 15 = 180.


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

In questo, si imposta l'ingresso sulla base 15 e l'uscita su 10 - nella base 15, quindi la base di uscita è 15. L'ingresso C0 nella base 15 è l'output C0 nella base 15.


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

Impostare l'ingresso sulla base 16, l'uscita sulla base 10 (A nella base 16 è 10 nella base 10).

C0 convertito in base 10 è: 12 * 16 = 192


La mia regola personale è di impostare prima obase, in modo da poter usare la base 10. Quindi impostare ibase, usando anche la base 10.

Nota che bcha un'eccezione ironica: ibase=Ae obase=Aimposta sempre input e output su base 10. Dalla bcpagina man:

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

Questo comportamento è sancito dalla specifica di bc: Dalla specifica 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.

Ecco perché l' ibase=Fimpostazione ha cambiato la base di input in base 15 e perché ho raccomandato di impostare sempre la base utilizzando la base 10. Evitare di confondersi.


@ StéphaneChazelas - Ho un ricordo di "ibase = A" che lavora su una macchina SysVr3 nel 1989 o giù di lì. Scommetto che risale più lontano alla singola specifica Unix. Non sono riuscito a trovare rapidamente un riferimento precedente.
Bruce Ediger,

Penso che sia perché ci sono più collegamenti intorno alle specifiche più vecchie poiché sono in circolazione da più tempo. Lo stesso tipo di cose accadono per la documentazione di apache / mysql / bugzilla ... dove google ti fornisce il documento per le versioni precedenti anziché prima.
Stéphane Chazelas,

5

Tutti i numeri sono interpretati da GNU bc come la base di input corrente che è in vigore per l'istruzione in cui appare il numero. Quando si utilizza una cifra all'esterno dell'input corrente interpretarli come la cifra più alta disponibile nella base (9 in decimale) quando parte di un numero a più cifre o come valori normali quando utilizzato come numero a una cifra ( A== 10 in decimale).

Dal manuale GNU bc :

I numeri a singola cifra hanno sempre il valore della cifra indipendentemente dal valore di ibase . (ovvero A = 10.) Per i numeri a più cifre, bccambia tutte le cifre di input maggiori o uguali a ibase nel valore di ibase -1. Questo rende il numero FFFsempre il più grande numero di 3 cifre della base di input.

Tuttavia, è necessario tenere presente che lo standard POSIX definisce questo comportamento solo per le assegnazioni a ibaseeobase , e non in qualsiasi altro contesto.

Dalla specifica SUS su bc :

Quando a ibase o obase viene assegnato un valore a singola cifra dall'elenco in Convenzioni lessicali in bc, il valore deve essere assunto in formato esadecimale. (Ad esempio, ibase = A imposta su base dieci, indipendentemente dal valore corrente di ibase .) In caso contrario, il comportamento non è definito quando nell'input compaiono cifre maggiori o uguali al valore di ibase . Sia ibase che obase devono avere valori iniziali di 10.

Il fattore chiave che ti manca è che F non è in realtà sedici, ma in realtà è quindici, quindi quando si imposta ibase = F si imposta la base di input su quindici.

Pertanto, per impostare il portabile ibase in esadecimale da uno stato sconosciuto, è quindi necessario utilizzare due prospetti: ibase=A; ibase=16. Tuttavia, all'inizio del programma puoi fare affidamento sul fatto che sia decimale e semplicemente utilizzarlo ibase=16.


+1: trucco carino con ibase=A; ibase=16.
Warren Young,


Avevo sempre pensato che il 6 e il 7 nelle intestazioni fossero la versione. Non ho mai visto nulla di diverso - quali sono il problema # 1-5?
Casuale 832

@ Random832: SUS e POSIX non sono la stessa cosa .
Warren Young,

@WarrenYoung SUS non incorpora POSIX? Questo paragrafo non ha tag di estensione e il documento dice cose come "parte di questo volume di POSIX.1-2008" in tutto.
Casuale 832

0

Si consiglia sempre di impostare ibasee obaseutilizzare un numero a una cifra, anziché un numero come 16, poiché in base alla bcpagina man,

I numeri a cifra singola hanno sempre il valore della cifra indipendentemente dal valore di ibase.

Ciò significa che A,B,...,Fhanno sempre i valori 10,11,...,15rispettivamente, indipendentemente da quale sia il valore di ibase. Puoi anche usare F+1per specificare il numero 16. Ad esempio, faresti meglio a scrivere

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

invece di scrivere echo "ibase=16; obase=A; C0" | bcper specificare che la base di input è 16e la base di output è 10. O per esempio, se vuoi entrambi ibasee obaseavere 16 anni, è meglio usarli

ibase=F+1; obase=F+1

invece di usare ibase=16; obase=10. Allo stesso modo, se hai intenzione di inserire i tuoi numeri nella base 14 e di generarli nella base 16, usa

ibase=E; obase=F+1

Sebbene le forme del bagno abbiano gli stessi risultati, la prima è meno soggetta a errori, mentre la seconda può portare a maggiore confusione ed errore.

La differenza tra le due forme diventa particolarmente evidente quando ci si trova nell'ambiente di esecuzione di bco si scriveranno i calcoli in un file e si passerà a quel file bccome argomento. In tali situazioni, potrebbe essere necessario modificare i valori di ibasee obasepiù volte, e l'utilizzo di quest'ultimo modulo può causare gravi confusioni ed errori. (Provalo)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.