我经常使用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
你真正想说的是:
$ echo "ibase=16; C0" | bc
192
对于十六进制到十进制,并且:
$ echo "obase=16; 192" | bc
C0
对于十进制到十六进制。
对于任何涉及十进制数的转换,您不需要同时给出ibase
和,因为这些设置默认为 10。obase
你做需要同时提供两者以进行二进制到十六进制等转换。在这种情况下,我发现如果你先给出以下内容,最容易理解事情obase
:
$ echo "obase=16; ibase=2; 11000000" | bc
C0
如果您ibase
先给出,它会更改以下设置的解释obase
,因此命令必须是:
$ echo "ibase=2; obase=10000; 11000000" | bc
C0
这是因为按照这个顺序,该obase
值被解释为二进制数,因此您需要给出 100002=16 才能获得十六进制输出。这太笨拙了。
现在让我们弄清楚为什么您的三个示例会出现这样的行为。
echo "ibase=F;obase=A;C0" | bc
180
这会将输入基数设置为 15,将输出基数设置为 10,因为个位数的值会以十六进制进行解释,根据 POSIX。这要求
bc
告诉您基数 A₁₅=10 中的 C0₁₅ 是什么,并且它正确回答了 180₁₀,尽管这肯定不是您想问的问题。echo "ibase=F;obase=10;C0" | bc
C0
这是以 15 为基数的空转换。
为什么?首先,因为单个
F
数字以十六进制解释,正如我在前面的示例中指出的那样。但现在您已将其设置为基数 15,以下输出基数设置将以这种方式解释,并且 10₁₅=15,因此您有从 C0₁₅ 到 C0₁₅ 的空转换。没错,输出不是您假设的十六进制,而是以 15 为基数!
您可以通过尝试转换
F0
而不是 来向自己证明这一点C0
。由于F
15 基数中没有数字,因此bc
将其钳制为,并作为输出E0
给出。E0
echo "ibase=16; obase=A; C0"
192
这是您的三个示例中唯一可能具有实际用途的示例。
它将输入基数更改为十六进制第一的,这样您就不再需要深入研究 POSIX 规范来理解为什么
A
被解释为十六进制,在本例中为 10。唯一的问题是,将输出基数设置为 A₁₆=10 是多余的,因为这是它的默认值。
答案2
设置ibase
意味着您需要obase
在同一基础上进行设置。解释你的例子将表明这一点:
echo "ibase=F;obase=A;C0" | bc
您可以bc
使用“ibase=F”将输入数字视为以 15 为基数的数字。“obase=A”将输出数字设置为以 10 为基数,这是默认值。
bc
将 C0 读取为以 15 为基数的数字:C = 12。12*15 = 180。
echo "ibase=F;obase=10;C0" | bc
在此例中,您将输入设置为基数 15,输出设置为基数 15 中的 10 -,因此输出基数为 15。基数 15 中的 C0 输入是基数 15 中的 C0 输出。
echo "ibase=16;obase=A;C0" | bc
将输入设置为基数 16,输出设置为基数 10(基数 16 中的 A 等于基数 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
:2004 OpenGroupbc
规范:
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 设置基数。避免让自己感到困惑。
答案3
所有数字都被 GNU bc 解释为当前输入基数,该基数对数字所在的语句有效。当您使用当前输入之外的数字时,将它们解释为基数中可用的最高数字(十进制中的 9)。多位数字的值,或用作单位数字时的正常值(A
== 10 十进制)。
来自GNU BC 手册:
无论 的值如何,单位数始终具有该数字的值数据库。 (即 A = 10。)对于多位数字,
bc
将所有输入数字更改为大于或等于数据库的值数据库-1。这使得该数字FFF
始终是输入基数中最大的 3 位数字。
但是,您应该注意,POSIX 标准仅针对对ibase
和 的赋值定义此行为obase
,而不是在任何其他上下文中定义此行为。
来自BC 上的 SUS 规格:
当任一数据库或者奥巴塞被分配了一个数字bc 中的词汇约定列表中的值,该值应假定为十六进制。 (例如,数据库=A 设置为以 10 为基数,无论当前电流如何数据库值。)否则,当数字大于或等于以下值时,行为未定义数据库出现在输入中。两个都数据库和奥巴塞初始值为 10。
您缺少的关键因素是 F 实际上不是十六,而是实际上十五,因此当您设置 ibase=F 时,您将输入基数设置为十五。
因此,要将 ibase 从未知状态可移植地设置为十六进制,您需要使用两个语句:ibase=A; ibase=16
。但是,在程序的开头,您可以相信它是十进制的,并且只需使用ibase=16
.
答案4
标题##简而言之,一旦设置了ibase,输入的所有数字都在该基数中。所以,如果你这样做:
ibase=16
然后您在此之后输入的任何数字都被视为十六进制数字。这样做可能会产生意想不到的结果,如果您随后尝试将 obase (或重置 ibase)设置为基数 10,则需要使用 A,而不是 10 作为数字:
obase=A
在 bc 中,您可以通过键入而不设置它们来检查 ibase 和 obase 的值:
ibase
16
obase
10
用 oneliner 的说法(将十六进制 13 转换为十进制:
echo "ibase=16;13;ibase;obase" | bc -l
19
16
10