Czy rozumiesz „ibase” i „obase” w przypadku konwersji za pomocą bc?


22

Często używam bcnarzędzia do konwersji szesnastkowej na dziesiętną i odwrotnie. Jednak zawsze jest trochę prób i błędów, jak ibasei obasenależy skonfigurować. Na przykład tutaj chcę przekonwertować wartość szesnastkową C0 na dziesiętną:

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

Jaka jest tutaj logika? obase( Aw moim trzecim przykładzie) musi znajdować się w tej samej bazie, co wartość, która jest konwertowana ( C0w moich przykładach), a ibase( 16w moim trzecim przykładzie) musi znajdować się w bazie, na którą dokonuję konwersji?


1
do obliczeń szesnastkowych (dane wejściowe i wyjściowe w postaci szesnastkowej) muszę ustawić obase przed ibase!
Paschalis

Odpowiedzi:


36

To, co naprawdę chcesz powiedzieć, to:

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

dla liczby szesnastkowej i dziesiętnej oraz:

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

dla dziesiętnego na szesnastkowy.

Nie musisz podawać zarówno, jak ibasei obaseżadnej konwersji z liczbami dziesiętnymi, ponieważ te ustawienia domyślnie wynoszą 10.

Ci nie muszą dać zarówno dla konwersji, takich jak binarny do hex. W takim przypadku uważam, że najłatwiej zrozumieć sens, jeśli podasz obasenajpierw:

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

Jeśli ibasezamiast tego podasz pierwszy, zmienia to interpretację następującego obaseustawienia, więc polecenie musi być:

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

Wynika to z tego, że w tej kolejności obasewartość jest interpretowana jako liczba binarna, więc musisz podać 10000₂ = 16, aby uzyskać wynik w postaci szesnastkowej. To niezręczne.


Teraz zastanówmy się, dlaczego twoje trzy przykłady zachowują się tak, jak one.

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

    180

    To ustawia podstawę wejściową na 15, a bazę wyjściową na 10, ponieważ wartość jednocyfrowa jest interpretowana w systemie szesnastkowym, zgodnie z POSIX . To prosi, bcaby powiedzieć ci, co C0₁₅ znajduje się w podstawie A₁₅ = 10 i odpowiada poprawnie 180₁₀, chociaż z pewnością nie jest to pytanie, które chciałeś zadać.

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

    C0

    Jest to konwersja zerowa w bazie 15.

    Czemu? Po pierwsze, ponieważ pojedyncza Fcyfra jest interpretowana szesnastkowo, jak wskazałem w poprzednim przykładzie. Ale teraz, gdy ustawiłeś go na bazę 15, następujące ustawienie wyjściowej bazy jest interpretowane w ten sposób, a 10₁₅ = 15, więc masz konwersję zerową z C0₁₅ na C0₁₅.

    Zgadza się, dane wyjściowe nie są szesnastkowe, jak zakładałeś, są w bazie 15!

    Możesz to sobie udowodnić, próbując przekonwertować F0zamiast C0. Ponieważ Fw podstawie 15 nie ma cyfry, bczaciska ją E0i podaje E0jako wynik.

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

    192

    To jedyny z trzech przykładów, który prawdopodobnie ma praktyczne zastosowanie.

    Najpierw zmienia bazę wejściową na heksadecymalną , dzięki czemu nie trzeba już zagłębiać się w specyfikację POSIX, aby zrozumieć, dlaczego Aw tym przypadku interpretowana jest jako hex, 10. Jedynym problemem jest to, że zbędne jest ustawienie bazy wyjściowej na A = 10, ponieważ jest to jej wartość domyślna.


7

Ustawienie ibaseoznacza, że ​​musisz ustawić obasew tej samej bazie. Wyjaśnienie przykładów pokaże to:

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

Ustawiono bcrozpatrywanie liczb wejściowych przedstawionych w podstawie 15 za pomocą „ibase = F”. „obase = A” ustawia liczby wyjściowe na podstawową 10, co jest wartością domyślną.

bc odczytuje C0 jako liczbę podstawową 15: C = 12. 12 * 15 = 180.


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

W tym ustawiasz wejście na bazę 15, a wyjście na 10 - w podstawie 15, więc podstawa wyjścia wynosi 15. Wejście C0 w bazie 15 to wyjście C0 w bazie 15.


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

Ustaw wejście na bazę 16, wyjście na bazę 10 (A w bazie 16 to 10 w bazie 10).

C0 przeliczone na podstawę 10 wynosi: 12 * 16 = 192


Moją osobistą zasadą jest ustawienie najpierw obase, aby móc korzystać z bazy 10. Następnie ustaw ibase, również używając bazy 10.

Zauważ, że bcma ironiczny wyjątek: ibase=Ai obase=Azawsze ustawia wejście i wyjście na bazę 10. Ze strony podręcznika bc:

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

To zachowanie jest zapisane w specyfikacji bc: Ze specyfikacji 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.

Dlatego ibase=Fustawienie zmieniło bazę wejściową na bazę 15 i dlatego zalecałem, aby zawsze ustawiać bazę za pomocą bazy 10. Unikaj mylenia siebie.


@ StéphaneChazelas - Mam wspomnienie o „ibase = A” działającym na maszynie SysVr3 w około 1989 roku. Założę się, że sięga dalej niż specyfikacja Single Unix. Nie mogłem szybko znaleźć w Google wcześniejszego ref.
Bruce Ediger

Myślę, że dzieje się tak, ponieważ istnieje więcej linków do starszych specyfikacji, ponieważ istnieją one dłużej. To samo dzieje się z dokumentacją apache / mysql / bugzilla ... dokumentacja, w której Google udostępnia dokument dla starszych wersji zamiast najnowszych.
Stéphane Chazelas

5

Wszystkie liczby są interpretowane przez GNU bc jako bieżąca podstawa wejściowa, która obowiązuje dla instrukcji, w której pojawia się liczba. Gdy użyjesz cyfry poza bieżącym wejściem, zinterpretuj je jako najwyższą cyfrę dostępną w bazie (9 dziesiętnie), gdy część liczby wielocyfrowej lub ich wartości normalnych, gdy jest stosowany jako liczba jednocyfrowa ( A== 10 miejsc po przecinku).

Z podręcznika GNU bc :

Liczby jednocyfrowe zawsze mają wartość cyfry niezależnie od wartości ibase . (tj. A = 10.) W przypadku liczb wielocyfrowych bczmienia wszystkie cyfry wejściowe większe lub równe ibase na wartość ibase -1. To sprawia, że ​​liczba ta FFFzawsze jest największą 3-cyfrową liczbą podstawy wprowadzania.

Należy jednak mieć świadomość, że standard POSIX definiuje tylko to zachowanie dla przypisania do ibasei obase, a nie w jakimkolwiek innym kontekście.

Ze specyfikacji SUS na bc :

Gdy do bazy ibase lub obase zostanie przypisana jednocyfrowa wartość z listy w konwencjach leksykalnych w bc, wartość przyjmuje się w systemie szesnastkowym. (Na przykład ibase = A ustawia na dziesięć, niezależnie od bieżącej wartości ibase .) W przeciwnym razie zachowanie nie jest zdefiniowane, gdy na wejściu pojawią się cyfry większe lub równe wartości ibase . Zarówno ibase, jak i obase powinny mieć wartości początkowe 10.

Kluczowym czynnikiem, którego brakuje, jest to, że F nie ma szesnastu, ale tak naprawdę piętnaście, więc kiedy ustawiasz ibase = F, ustawiasz bazę wejściową na piętnaście.

Dlatego, aby przenośnie ustawić ibase do szesnastkowym z nieznanego stanu, dlatego trzeba używać dwa oświadczenia: ibase=A; ibase=16. Jednak na początku programu można polegać na systemie dziesiętnym i po prostu go używać ibase=16.


+1: Urocza sztuczka z ibase=A; ibase=16.
Warren Young,


Zawsze myślałem, że 6 i 7 w nagłówkach są wersją. Nigdy nie widziałem nic innego - jakie są problemy nr 1-5?
Random832

@ Random832: SUS i POSIX to nie to samo .
Warren Young,

@WarrenYoung Czy SUS nie zawiera POSIX? W tym akapicie nie ma znacznika rozszerzenia, a dokument mówi przez cały czas takie rzeczy jak „część tego tomu POSIX.1-2008”.
Random832

0

Zawsze zaleca się ustawienie ibasei obaseużywanie liczby jednocyfrowej zamiast liczby takiej jak 16, ponieważ zgodnie ze bcstroną podręcznika,

Liczby jednocyfrowe zawsze mają wartość cyfry niezależnie od wartości ibase.

Oznacza to, że A,B,...,Fzawsze mają 10,11,...,15odpowiednio wartości , niezależnie od wartości ibase. Możesz także użyć F+1do podania liczby 16. Na przykład lepiej pisz

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

zamiast pisać, echo "ibase=16; obase=A; C0" | bcaby określić, że podstawa wejściowa jest, 16a wyjściowa podstawa to 10. Lub, na przykład, jeśli chcesz mieć oba ibasei obasemieć 16 lat, lepiej użyj

ibase=F+1; obase=F+1

zamiast używać ibase=16; obase=10. Podobnie, jeśli zamierzasz wprowadzić swoje liczby w bazie 14 i wyprowadzić je w bazie 16, użyj

ibase=E; obase=F+1

Chociaż formy kąpieli mają takie same wyniki, te pierwsze są mniej podatne na błędy, podczas gdy te drugie mogą prowadzić do większego zamieszania i błędów.

Różnica między tymi dwiema postaciami staje się bardziej widoczna, gdy jesteś w środowisku wykonawczym bclub zamierzasz zapisać swoje obliczenia w pliku, a następnie przekazać ten plik bcjako argument. W takich sytuacjach może trzeba zmienić wartości ibasei obasekilka razy, i za pomocą tej ostatniej formy, może prowadzić do poważnych nieporozumień i błędów. (Doświadcz tego)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.