Memahami "ibase" dan "obase" jika terjadi konversi dengan bc?

22

Saya sering menggunakan bcutilitas untuk mengubah hex menjadi desimal dan sebaliknya. Namun, selalu sedikit trial and error bagaimana ibasedan obaseharus dikonfigurasi. Sebagai contoh di sini saya ingin mengonversi nilai hex C0 ke desimal:

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

Apa logikanya di sini? obase( Adalam contoh ketiga saya) harus dalam basis yang sama dengan nilai yang dikonversi ( C0dalam contoh saya) dan ibase( 16dalam contoh ketiga saya) harus di basis di mana saya mengkonversi?

Martin
sumber
1
untuk perhitungan hex (input dan output dalam hex) Saya harus menetapkan obase sebelum ibase!
Paschalis

Jawaban:

36

Apa yang sebenarnya ingin Anda katakan adalah:

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

untuk hex-to-desimal, dan:

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

untuk desimal ke hex.

Anda tidak perlu memberikan keduanya ibasedan obaseuntuk konversi apa pun yang melibatkan angka desimal, karena pengaturan ini default ke 10.

Anda tidak perlu memberikan baik untuk konversi seperti biner-to-hex. Dalam hal ini, saya merasa paling mudah untuk memahami sesuatu jika Anda memberi obasepertama:

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

Jika Anda memberi ibasepertama sebagai gantinya, itu mengubah interpretasi obasepengaturan berikut , sehingga perintahnya harus:

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

Ini karena dalam urutan ini, obasenilai ditafsirkan sebagai angka biner, jadi Anda harus memberikan 10.000₂ = 16 untuk mendapatkan output dalam hex. Itu canggung.


Sekarang mari kita cari tahu mengapa tiga contoh Anda berperilaku seperti itu.

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

    180

    Itu menetapkan basis input ke 15 dan basis output ke 10, karena nilai satu digit ditafsirkan dalam hex, menurut POSIX . Ini meminta bcAnda memberi tahu Anda apa yang C0₁₅ ada di basis A₁₅ = 10, dan menjawab dengan benar 180 this, meskipun ini jelas bukan pertanyaan yang ingin Anda tanyakan.

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

    C0

    Ini adalah konversi nol di basis 15.

    Mengapa? Pertama, karena Fdigit tunggal ditafsirkan dalam hex, seperti yang saya tunjukkan dalam contoh sebelumnya. Tetapi sekarang setelah Anda mengaturnya ke basis 15, pengaturan basis output berikut ditafsirkan seperti itu, dan 10₁₅ = 15, sehingga Anda memiliki konversi nol dari C0₁₅ ke C0₁₅.

    Itu benar, output tidak dalam hex seperti yang Anda asumsikan, itu di base 15!

    Anda dapat membuktikan ini pada diri Anda sendiri dengan mencoba mengonversi F0alih-alih C0. Karena tidak ada Fdigit di basis 15, bcklem ke E0, dan berikan E0sebagai output.

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

    192

    Ini adalah satu-satunya dari tiga contoh Anda yang mungkin memiliki penggunaan praktis.

    Ini mengubah basis input menjadi hex terlebih dahulu , sehingga Anda tidak perlu lagi menggali spec POSIX untuk memahami mengapa Aditafsirkan sebagai hex, 10 dalam hal ini. Satu-satunya masalah dengan itu adalah redundan untuk mengatur basis output ke A₁₆ = 10, karena itulah nilai defaultnya.

Warren Young
sumber
7

Pengaturan ibaseberarti Anda perlu mengatur obasebasis yang sama. Menjelaskan contoh Anda akan menunjukkan ini:

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

Anda mengatur bcuntuk mempertimbangkan nomor input sebagaimana diwakili dalam basis 15 dengan "ibase = F". "obase = A" mengatur nomor output ke basis 10, yang merupakan default.

bc membaca C0 sebagai angka dasar 15: C = 12. 12 * 15 = 180.


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

Dalam yang satu ini, Anda menetapkan input ke basis 15, dan output ke 10 - di basis 15, sehingga basis output adalah 15. C0 input di basis 15 adalah C0 output di basis 15.


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

Atur input ke basis 16, output ke basis 10 (A dalam basis 16 adalah 10 di basis 10).

C0 dikonversi ke basis 10 adalah: 12 * 16 = 192


Aturan pribadi saya adalah mengatur obase terlebih dahulu, sehingga saya bisa menggunakan basis 10. Kemudian atur ibase, juga menggunakan basis 10.

Catatan yang bcmemang memiliki pengecualian ironis: ibase=Adan obase=Aselalu menetapkan input dan output ke basis 10. Dari bchalaman manual:

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

Perilaku ini diabadikan dalam spesifikasi bc: Dari spesifikasi 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.

Itu sebabnya ibase=Fpengaturan mengubah basis input Anda ke basis 15, dan mengapa saya menyarankan untuk selalu mengatur basis menggunakan basis 10. Hindari membingungkan diri sendiri.

Bruce Ediger
sumber
@ StéphaneChazelas - Saya memiliki ingatan "ibase = A" yang bekerja pada mesin SysVr3 pada tahun 1989 atau lebih. Saya yakin itu akan kembali lebih jauh bahwa Single Unix Spec. Saya tidak dapat dengan cepat mencantumkan referensi sebelumnya.
Bruce Ediger
Saya pikir itu karena ada lebih banyak tautan di sekitar ke spesifikasi yang lebih lama karena sudah ada lebih lama. Hal yang sama terjadi untuk dokumentasi apache / mysql / bugzilla ... di mana google memberi Anda dokumen untuk versi yang lebih lama lebih dulu, bukan yang terbaru.
Stéphane Chazelas
5

Semua angka ditafsirkan oleh GNU bc sebagai basis input saat ini yang berlaku untuk pernyataan nomor muncul. Ketika Anda menggunakan digit di luar input saat ini mengartikannya sebagai digit tertinggi yang tersedia di basis (9 dalam desimal) ketika bagian dari beberapa digit angka, atau sebagai nilai normalnya ketika digunakan sebagai angka digit tunggal ( A== 10 dalam desimal).

Dari manual GNU bc :

Angka digit tunggal selalu memiliki nilai digit terlepas dari nilai ibase . (Yaitu A = 10.) Untuk angka multi-digit, ubahbc semua digit input lebih besar atau sama dengan ibase ke nilai ibase -1. Ini membuat angka FFFselalu menjadi angka 3 digit terbesar dari basis input.

Namun, Anda harus menyadari bahwa standar POSIX hanya mendefinisikan perilaku ini untuk penugasan ke ibasedan obase, dan tidak dalam konteks lain.

Dari spesifikasi SUS pada bc :

Ketika ibase atau obase diberikan nilai satu digit dari daftar dalam Konvensi Lexical di bc, nilai tersebut harus diasumsikan dalam heksadesimal. (Misalnya, ibase = A mengatur ke basis sepuluh, terlepas dari nilai ibase saat ini .) Jika tidak, perilaku tidak terdefinisi ketika angka lebih besar dari atau sama dengan nilai ibase muncul di input. Baik ibase dan obase harus memiliki nilai awal 10.

Faktor utama yang Anda lewatkan adalah bahwa F sebenarnya bukan enam belas, tetapi sebenarnya lima belas, jadi ketika Anda menetapkan ibase = F Anda menetapkan basis input ke lima belas.

Oleh karena itu, untuk portable mengatur Ibase ke heksadesimal dari negara yang tidak dikenal, karena itu Anda perlu menggunakan dua pernyataan: ibase=A; ibase=16. Namun, di awal program Anda dapat mengandalkannya sebagai desimal dan cukup digunakan ibase=16.

Random832
sumber
+1: Trik lucu dengan ibase=A; ibase=16.
Warren Young
Itu SUSv3, bukan SUSv6. SUSv4 ada di pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html
Stéphane Chazelas
Saya selalu berpikir 6 dan 7 di header adalah versi. Saya belum pernah melihat yang berbeda - apa masalah # 1-5?
Random832
@ Random832: SUS dan POSIX bukan hal yang sama .
Warren Young
@ WarrenYoung Tidak SUS menggabungkan POSIX? Paragraf ini tidak memiliki tag ekstensi, dan dokumen mengatakan hal-hal seperti "bagian dari volume POSIX.1-2008" ini.
Random832
0

Itu selalu disarankan untuk mengatur ibasedan obasemenggunakan nomor satu digit, daripada angka seperti 16, karena menurut bchalaman manual,

Angka digit tunggal selalu memiliki nilai digit terlepas dari nilai ibase.

Ini berarti bahwa A,B,...,Fselalu memiliki nilai 10,11,...,15masing-masing, terlepas dari apa nilainya ibase. Anda juga dapat menggunakan F+1untuk menentukan nomor 16. Misalnya, Anda sebaiknya menulis

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

alih-alih menulis echo "ibase=16; obase=A; C0" | bcuntuk menentukan basis input 16dan basis output 10. Atau misalnya, jika Anda menginginkan keduanya ibasedan obaseberusia 16 tahun, sebaiknya Anda gunakan

ibase=F+1; obase=F+1

alih-alih menggunakan ibase=16; obase=10. Demikian pula, jika Anda akan memasukkan angka-angka di basis 14 dan menghasilkannya di basis 16, gunakan

ibase=E; obase=F+1

Meskipun bentuk mandi memiliki hasil yang sama, yang pertama kurang rawan kesalahan, sedangkan yang terakhir dapat menyebabkan lebih banyak kebingungan dan kesalahan.

Perbedaan antara dua bentuk terutama menjadi lebih jelas, ketika Anda berada di lingkungan eksekusi bc, atau Anda akan menulis perhitungan Anda dalam file, dan kemudian meneruskan file itu bcsebagai argumen. Dalam situasi seperti itu, Anda mungkin harus mengubah nilai ibasedan obasebeberapa kali, dan menggunakan formulir yang terakhir, dapat menyebabkan kebingungan dan kesalahan serius. (mengalaminya)

Hedayat Mahdipour
sumber