Понять «ibase» и «obase» в случае конвертации с bc?

22

Я часто использую bcутилиту для преобразования шестнадцатеричного числа в десятичное и наоборот. Тем не менее, это всегда бит проб и ошибок, как ibaseи obaseдолжно быть настроено. Например, здесь я хочу преобразовать шестнадцатеричное значение C0 в десятичное:

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

В чем тут логика? obase( Aв моем третьем примере) должно быть в той же базе, что и преобразованное значение ( C0в моих примерах), и ibase( 16в моем третьем примере) должно быть в базе, в которую я преобразую?

Мартин
источник
1
для шестнадцатеричных вычислений (ввод и вывод в шестнадцатеричном формате) я должен установить obase перед ibase!
Пасхалис

Ответы:

36

То, что вы на самом деле хотите сказать, это:

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

для шестнадцатеричной системы счисления и:

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

для десятичного в шестнадцатеричное.

Вам не нужно указывать и то, ibaseи другое obaseдля преобразования с десятичными числами, так как эти настройки по умолчанию равны 10.

Вы действительно должны дать как для таких преобразований , как двоичные-на-шестнадцатеричный. В этом случае мне проще всего разобраться, если вы дадите obaseсначала:

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

Если ibaseвместо этого вы дадите сначала, это изменит интерпретацию следующего obaseпараметра, поэтому команда должна быть:

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

Это связано с тем, что в этом порядке obaseзначение интерпретируется как двоичное число, поэтому необходимо получить 10000 hex = 16, чтобы получить вывод в шестнадцатеричном виде. Это неуклюже


Теперь давайте выясним, почему ваши три примера ведут себя так, как они.

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

    180

    Это устанавливает для входной базы значение 15, а для выходной базы - 10, поскольку однозначное значение интерпретируется в шестнадцатеричном виде, в соответствии с POSIX . Это попросит bcвас сказать, что такое C0₁₅ в базе A₁₅ = 10, и он правильно отвечает на 180₁₀, хотя это, конечно, не тот вопрос, который вы хотели задать.

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

    C0

    Это нулевое преобразование в базе 15.

    Зачем? Во-первых, потому что одна Fцифра интерпретируется в шестнадцатеричном виде, как я указывал в предыдущем примере. Но теперь, когда вы установили его на основание 15, следующий выходной базовый параметр интерпретируется таким образом, и 10₁₅ = 15, так что у вас есть нулевое преобразование из C0₁₅ в C0₁₅.

    Правильно, вывод не в шестнадцатеричном виде, как вы предполагали, он в базе 15!

    Вы можете доказать это себе, пытаясь преобразовать F0вместо C0. Поскольку Fв базе 15 нет цифры, bcона фиксируется E0и выдается E0в качестве выходного значения.

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

    192

    Это единственный из ваших трех примеров, который, вероятно, имеет какое-либо практическое применение.

    Он сначала меняет базу ввода на hex , так что вам больше не нужно копаться в спецификации POSIX, чтобы понять, почему Aв данном случае интерпретируется как hex, 10. Единственная проблема заключается в том, что излишне устанавливать выходную базу равной A₁₆ = 10, поскольку это ее значение по умолчанию.

Уоррен Янг
источник
7

Установка ibaseозначает, что вам нужно установить obaseв той же базе. Объяснение ваших примеров покажет это:

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

Вы bcдолжны учитывать входные числа, представленные в базе 15 с «ibase = F». «obase = A» устанавливает выходные значения в базу 10, которая используется по умолчанию.

bc читает C0 как базовое число 15: C = 12. 12 * 15 = 180.


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

В этом случае вы устанавливаете вход для базы 15, а выход для 10 - для базы 15, поэтому выходной базой является 15. Вход C0 в базе 15 - это выход C0 в базе 15.


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

Установите вход на базу 16, выход на базу 10 (A в базе 16 - 10 в базе 10).

C0, преобразованный в основание 10: 12 * 16 = 192


Мое личное правило - сначала установить obase, чтобы я мог использовать базу 10. Затем установите ibase, также используя базу 10.

Обратите внимание, что bcимеет ироническое исключение: ibase=Aи obase=Aвсегда устанавливает ввод и вывод в базу 10. На bcстранице руководства :

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

Это поведение закреплено в спецификации bc: Из спецификации OpenGroup 2004 годаbc :

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.

Вот почему ibase=Fнастройка изменила вашу базу ввода на базу 15, и поэтому я рекомендовал всегда устанавливать базу, используя базу 10. Избегайте путаницы.

Брюс Эдигер
источник
@ StéphaneChazelas - у меня есть воспоминания о «ibase = A», работавшем на машине SysVr3 в 1989 году или около того. Могу поспорить, что он возвращается еще дальше, чем Single Unix Spec. Я не мог быстро погуглить более ранний реф.
Брюс Эдигер
Я думаю, это потому, что есть больше ссылок на старые спецификации, так как они существуют дольше. То же самое происходит с документацией apache / mysql / bugzilla ..., где google выдает сначала документ для старых версий, а не для последних.
Стефан Шазелас
5

Все числа интерпретируются GNU bc как текущая база ввода, которая действует для оператора, в котором появляется число. Когда вы используете цифру вне текущего ввода, интерпретируйте их как самую высокую цифру, доступную в базе (9 в десятичной дроби), когда часть из нескольких цифр или в качестве их нормальных значений при использовании в качестве однозначного числа ( A== 10 в десятичном виде).

Из руководства GNU BC :

Однозначные числа всегда имеют значение цифры независимо от значения ibase . (т. е. A = 10.) Для многозначных чисел bcизменяет все входные цифры, большие или равные ibase, на значение ibase -1. Это делает число FFFвсегда самым большим 3-значным номером входной базы.

Однако вы должны знать, что стандарт POSIX определяет это поведение только для присваиваний ibaseи obase, а не в каком-либо другом контексте.

Из спецификации SUS на bc :

Когда либо IBase или obase присваивается один значное значение из списка в лексических конвенций в БЛ, значение должно быть принято в шестнадцатеричном формате. (Например, ibase = A устанавливается равным десяти, независимо от текущего значения ibase .) В противном случае поведение не определено, когда на входе появляются цифры, которые больше или равны значению ibase . И ibase, и obase должны иметь начальные значения 10.

Ключевым фактором, который вам не хватает, является то, что F на самом деле не шестнадцать, а на самом деле пятнадцать, поэтому, когда вы устанавливаете ibase = F, вы устанавливаете базу ввода на пятнадцать.

Поэтому, чтобы переносимый установить IBase в шестнадцатеричный из неизвестного государства, то , следовательно , необходимо использовать два заявление: ibase=A; ibase=16. Тем не менее, в начале программы вы можете положиться на десятичность и просто использовать ibase=16.

Random832
источник
+1: милый трюк с ibase=A; ibase=16.
Уоррен Янг
Это SUSv3, а не SUSv6. SUSv4 находится по адресу pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html
Стефан
Я всегда думал, что 6 и 7 в заголовках были версией. Я никогда не видел ничего другого - что за проблема № 1-5?
Random832
@ Random832: SUS и POSIX - это не одно и то же .
Уоррен Янг
@WarrenYoung Не включает ли SUS POSIX? Этот абзац не имеет тега расширения, и документ повсюду говорит такие вещи, как «часть этого тома POSIX.1-2008».
Random832
0

Всегда рекомендуется устанавливать ibaseи obaseиспользовать однозначное число, а не число, такое как 16, согласно bcстранице руководства ,

Однозначные числа всегда имеют значение цифры независимо от значения ibase.

Это означает, что A,B,...,Fвсегда имеют значения 10,11,...,15соответственно, независимо от значения ibase. Вы также можете использовать, F+1чтобы указать номер 16. Например, вам лучше написать

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

вместо записи echo "ibase=16; obase=A; C0" | bcуказать, что база ввода есть, 16а база вывода есть 10. Или, например, если вы хотите, чтобы ibaseи obase16, и вам лучше использовать

ibase=F+1; obase=F+1

вместо того, чтобы использовать ibase=16; obase=10. Точно так же, если вы собираетесь ввести свои числа в базу 14 и вывести их в базу 16, используйте

ibase=E; obase=F+1

Хотя формы для ванны имеют одинаковые результаты, первая менее подвержена ошибкам, а вторая может привести к большей путанице и ошибкам.

Разница между этими двумя формами становится особенно очевидной, когда вы находитесь в среде выполнения bcили собираетесь записать свои вычисления в файл, а затем передать этот файл bcв качестве аргумента. В таких ситуациях вам может потребоваться изменить значения ibaseи obaseнесколько раз, и использование последней формы может привести к серьезной путанице и ошибкам. (испытать это)

Хедаят Махдипур
источник