Verstehen Sie "ibase" und "obase" bei Konvertierungen mit bc?

22

Ich verwende oft bcDienstprogramme zum Konvertieren von Hexadezimalzahlen in Dezimalzahlen und umgekehrt. Es ist jedoch immer ein bisschen Versuch und Irrtum, wie ibaseund obasesollte konfiguriert werden. Zum Beispiel möchte ich hier den Hex-Wert C0 in Dezimal umwandeln:

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

Was ist die Logik hier? obase( Ain meinem dritten Beispiel) muss sich in derselben Basis befinden wie der Wert, der konvertiert wird ( C0in meinen Beispielen), und ibase( 16in meinem dritten Beispiel) muss sich in der Basis befinden, in die ich konvertiere.

Martin
quelle
1
für hex-berechnungen (eingabe und ausgabe in hex) muss ich obase vor ibase setzen!
Paschalis

Antworten:

36

Was Sie eigentlich sagen wollen, ist:

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

für hex-to-decimal und:

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

für Dezimal-zu-Hex.

Sie müssen nicht beide ibaseund obasefür jede Konvertierung mit Dezimalzahlen angeben, da diese Einstellungen standardmäßig 10 sind.

Sie haben für beide Umsetzungen wie Binär-Hex geben müssen. In diesem Fall ist es für mich am einfachsten, die Dinge zu verstehen, wenn Sie obasezuerst Folgendes angeben :

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

Wenn Sie ibasestattdessen zuerst angeben, ändert sich die Interpretation der folgenden obaseEinstellung, sodass der Befehl lauten muss:

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

Dies liegt daran, dass in dieser Reihenfolge der obaseWert als Binärzahl interpretiert wird. Geben Sie also 10000₂ = 16 ein, um die Ausgabe in hexadezimaler Form zu erhalten. Das ist ungeschickt.


Lassen Sie uns nun herausfinden, warum sich Ihre drei Beispiele so verhalten, wie sie es tun.

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

    180

    Das setzt die Eingabebasis auf 15 und die Ausgabebasis auf 10, da ein einstelliger Wert gemäß POSIX in hexadezimaler Schreibweise interpretiert wird . Dies fragt bcSie, was C0 & sub1; & sub0; in der Basis A & sub1; & sub0; = 10 ist, und es beantwortet 180 & sub1; & sub0; richtig, obwohl dies sicherlich nicht die Frage ist, die Sie stellen wollten.

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

    C0

    Dies ist eine Nullkonvertierung in Basis 15.

    Warum? Erstens, weil die einzelne FZiffer hexadezimal interpretiert wird, wie ich im vorherigen Beispiel ausgeführt habe. Aber jetzt, da Sie es auf Basis 15 gesetzt haben, wird die folgende Ausgabe-Basiseinstellung auf diese Weise interpretiert, und 10 & sub1; & sub0; = 15, so daß Sie eine Nullumwandlung von C0 & sub1; & sub0;

    Richtig, die Ausgabe ist nicht hexadezimal, wie Sie angenommen haben, sondern in Basis 15!

    Sie können sich das selbst beweisen, indem Sie versuchen, F0statt zu konvertieren C0. Da die FBasis 15 keine Ziffer enthält, wird sie bcgeklemmt E0und E0als Ausgabe ausgegeben.

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

    192

    Dies ist das einzige Ihrer drei Beispiele, das wahrscheinlich einen praktischen Nutzen hat.

    Es verändert die Eingangsbasis hex zuerst , so dass Sie nicht mehr benötigen , in dem verstehen spec POSIX zu graben , warum Aals Hex interpretiert, 10 in diesem Fall. Das einzige Problem dabei ist, dass es redundant ist, die Ausgangsbasis auf A & sub1; & sub0; = 10 zu setzen, da dies der Standardwert ist.

Warren Young
quelle
7

Einstellung ibasebedeutet, obasedass Sie dieselbe Basis einstellen müssen . Wenn Sie Ihre Beispiele erläutern, sehen Sie Folgendes:

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

Sie legen fest bc, Eingabezahlen wie in Basis 15 mit "ibase = F" dargestellt zu berücksichtigen. "obase = A" setzt die Ausgangsnummern auf Basis 10, was die Standardeinstellung ist.

bc liest C0 als Zahl zur Basis 15: C = 12. 12 * 15 = 180.


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

In diesem Fall setzen Sie die Eingabe auf Basis 15 und die Ausgabe auf 10 - in Basis 15, sodass die Ausgabebasis 15 ist. Die Eingabe von C0 in Basis 15 ist die Ausgabe von C0 in Basis 15.


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

Setzen Sie den Eingang auf Basis 16 und den Ausgang auf Basis 10 (A in Basis 16 ist 10 in Basis 10).

C0, das zu Basis 10 konvertiert wird, ist: 12 * 16 = 192


Meine persönliche Regel ist es, zuerst obase zu setzen, damit ich base 10 verwenden kann. Dann setze ibase, ebenfalls base 10.

Beachten Sie, dass bceine ironische Ausnahme hat: ibase=Aund obase=Aimmer setzt die Ein- und Ausgabe Basis 10. Aus der bcManpage:

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

Dieses Verhalten ist in den folgenden Spezifikationen verankert bc: Aus der OpenGroup- bcSpezifikation von 2004 :

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.

Aus diesem Grund hat die ibase=FEinstellung Ihre Eingabebasis in Basis 15 geändert, und ich habe empfohlen, die Basis immer mit Basis 10 festzulegen. Vermeiden Sie es, sich selbst zu verwirren.

Bruce Ediger
quelle
@ StéphaneChazelas - Ich erinnere mich, dass "ibase = A" 1989 oder so auf einer SysVr3-Maschine gearbeitet hat. Ich wette, es geht weiter zurück als Single Unix Spec. Ich konnte eine frühere Referenz nicht schnell googeln.
Bruce Ediger
Ich denke, das liegt daran, dass es mehr Links zu den älteren Spezifikationen gibt, seit sie länger existieren. Ähnliches gilt für die Dokumentation zu apache / mysql / bugzilla ..., bei der Sie bei google das Dokument für die älteren Versionen zuerst anstatt für die neuesten Versionen erhalten.
Stéphane Chazelas
5

Alle Zahlen werden von GNU bc als die aktuelle Eingabebasis interpretiert, die für die Anweisung gilt, in der die Zahl erscheint. Wenn Sie eine Ziffer außerhalb der aktuellen Eingabe verwenden, interpretieren Sie sie als die höchste in der Basis verfügbare Ziffer (9 in Dezimalform), wenn Teil einer mehrstelligen Zahl oder als ihre normalen Werte, wenn sie als einstellige Zahl verwendet werden ( A== 10 in Dezimalzahl).

Aus dem GNU bc Handbuch :

Einzelne Ziffern haben immer den Wert der Ziffer, unabhängig vom Wert von ibase . (dh A = 10.) Ändert bei mehrstelligen Zahlen bcalle Eingabestellen, die größer oder gleich ibase sind, in den Wert von ibase -1. Dadurch ist die Nummer FFFimmer die größte dreistellige Nummer der Eingabebasis.

Beachten Sie jedoch, dass der POSIX-Standard dieses Verhalten nur für Zuweisungen an ibaseund definiert obaseund nicht in einem anderen Kontext.

Aus der SUS-Spezifikation auf bc :

Wenn ibase oder obase einen einstelligen Wert aus der Liste der Lexikalischen Konventionen in bc zugewiesen bekommen , wird der Wert hexadezimal angenommen. (Zum Beispiel wird ibase = A unabhängig vom aktuellen ibase- Wert auf die Basis zehn gesetzt .) Andernfalls ist das Verhalten nicht definiert, wenn Ziffern, die größer oder gleich dem Wert von ibase sind , in der Eingabe angezeigt werden. Sowohl ibase als auch obase müssen Anfangswerte von 10 haben.

Der Schlüsselfaktor, den Sie vermissen, ist, dass F nicht sechzehn, sondern fünfzehn ist. Wenn Sie also ibase = F einstellen, setzen Sie die Eingabebasis auf fünfzehn.

Deshalb, um portably die ibase in hexadezimalen von einem unbekannten Zustand versetzt, müssen Sie also zwei Aussagen verwenden: ibase=A; ibase=16. Zu Beginn des Programms können Sie sich jedoch darauf verlassen, dass es dezimal ist und einfach verwendet wird ibase=16.

Random832
quelle
+1: Süßer Trick mit ibase=A; ibase=16.
Warren Young
Das ist SUSv3, nicht SUSv6. SUSv4 finden Sie unter pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html
Stéphane Chazelas,
Ich hatte immer gedacht, dass die 6 und 7 in den Überschriften die Version sind. Ich habe noch nie etwas anderes gesehen - was sind die Probleme Nr. 1-5?
Random832
@ Random832: SUS und POSIX sind nicht dasselbe .
Warren Young
@WarrenYoung Beinhaltet SUS kein POSIX? Dieser Absatz hat kein Extension-Tag, und das Dokument sagt durchgehend Dinge wie "Teil dieses Bandes von POSIX.1-2008".
Random832
0

Es wird immer empfohlen , Satz ibaseund obaseeine einstellige Zahl verwenden, statt einer Nummer wie 16, da nach bcManpage,

Einzelne Ziffern haben immer den Wert der Ziffer, unabhängig vom Wert von ibase.

Dies bedeutet, dass A,B,...,Fimmer die entsprechenden Werte vorhanden 10,11,...,15sind, unabhängig davon, wie hoch der Wert ibaseist. Sie können auch verwenden F+1, um die Nummer anzugeben 16. Zum Beispiel solltest du besser schreiben

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

Anstatt zu schreiben echo "ibase=16; obase=A; C0" | bc, geben Sie an, dass es sich bei der Eingabebasis um 16und bei der Ausgabebasis um handelt 10. Oder wenn Sie zum Beispiel beide möchten ibaseund obase16 Jahre alt sein möchten , sollten Sie dies besser verwenden

ibase=F+1; obase=F+1

anstatt zu verwenden ibase=16; obase=10. Wenn Sie Ihre Zahlen in Basis 14 eingeben und in Basis 16 ausgeben möchten, verwenden Sie ebenfalls

ibase=E; obase=F+1

Obwohl Badeformen die gleichen Ergebnisse erzielen, ist erstere weniger fehleranfällig, während letztere zu mehr Verwirrung und Fehlern führen kann.

Der Unterschied zwischen den beiden Formen wird besonders deutlich, wenn Sie sich in der Ausführungsumgebung von befinden bcoder Ihre Berechnungen in eine Datei schreiben und diese Datei dann bcals Argument übergeben. In solchen Situationen müssen Sie möglicherweise die Werte von ibaseund obasemehrmals ändern , und die Verwendung des letzteren Formulars kann zu ernsthaften Verwirrungen und Fehlern führen. (erlebe es)

Hedayat Mahdipour
quelle