Skip to content

实现任意进制转十进制

  发布于2022-08-17 16:39
保质期2025-08
新鲜度
19%

十六进制转十进制

思路

分析一下十六进制转十进制的过程。以十六进制整数 D0A1D2ED 为例,

字符位置01234567
十六进制D0A1D2ED
十进制1301011321413
权重167166165164163162161160

用每一位的十进制乘以权重后的和就是对应的十进制整数。

3500266221=13×167+0×166+10×165+1×164+13×163+2×162+14×161+13×160

其中,每一位的十进制,刚好是每一位的十六进制字符,在字符集 0123456789ABCDEF 中出现的位置,

0123456789ABCDEF
0123456789101112131415
D13
00
A10
11
D13
22
E14
D13

实现

十六进制整数 D0A1D2ED 长度为8,权重的幂是0~7,因此可以使用 range(n) 生成一个 [0,n) 的递增数列作为权重的幂。

python
integer = "D0A1D2ED"
length = len(integer)  # length == 8
powers = range(length)  # list(powers) == [0, 1, 2, 3, 4, 5, 6, 7]

十六进制对应的十进制,刚好是字符在字符集中的位置,比如 0 在第0个位置,A 在第10个位置。使用字符串的方法 index() 即可实现。

python
integer = "D0A1D2ED"
charset = "0123456789ABCDEF"
digits = [charset.index(char) for char in integer]
# digits == [13, 0, 10, 1, 13, 2, 14, 13]
# 对应的字符:  D  0   A  1   D  2   E   D

接下来需要将十进制和权重使用 zip() 组合在一起,方便后续计算。注意权重和十进制的顺序是相反的。因为权重的底数是固定值,所以这里不添加到组合中。

python
integer = "D0A1D2ED"
charset = "0123456789ABCDEF"
digits = [charset.index(char) for char in integer][::-1]
# digits = [13, 14, 2, 13, 1, 10, 0, 13]

powers = range(len(integer))
# list(powers) == [0, 1, 2, 3, 4, 5, 6, 7]

pairs = zip(digits, powers)
# list(pairs) == [(13,0), (14,1), (2,2), (13,3), (1,4), (10,5), (0,6), (13,7)]

每个 pair 需要按照以下关系进行计算,然后对所有 product 进行求和,即可得到最终结果。

productp=pair0×16pair1

使用Python表达如下:

python
def multiply(pair):
    product = pair[0] * 16 ** pair[1]
    return product

转译为匿名函数如下:

python
lambda pair: pair[0] * 16 ** pair[1]

使用 map()pair 求值为 product。map 作动词即为映射,这里便是将这个关系应用在每一个元素上,得到计算后的值。

python
integer = "D0A1D2ED"
charset = "0123456789ABCDEF"
digits = [charset.index(char) for char in integer][::-1]
# digits = [13, 14, 2, 13, 1, 10, 0, 13]

powers = range(len(integer))
# list(powers) == [0, 1, 2, 3, 4, 5, 6, 7]

pairs = zip(digits, powers)
# list(pairs) == [(13,0), (14,1), (2,2), (13,3), (1,4), (10,5), (0,6), (13,7)]

products = map(lambda pair: pair[0] * 16 ** pair[1], pairs)
# list(products) == [13, 224, 512, 53248, 65536, 10485760, 0, 3489660928]

summary = sum(products)
# summary == 3500266221

最后使用 sum() 求所有 product 的总和,即是结果。sum 是 summary的缩写,是对有限的数列进行求和。换句话说,它相当于使用了 + 对所有元素进行拼接,并计算拼接出来的表达式。

任意进制转十进制

integer 为参数,将上述代码封装到函数中:

python
def n2hex(integer):
    charset = "0123456789ABCDEF"
    digits = [charset.index(char) for char in integer][::-1]
    powers = range(len(integer))
    pairs = zip(digits, powers)
    products = map(lambda pair: pair[0] * 16 ** pair[1], pairs)
    summary = sum(products)
    return summary

不难看出,字符集 charset 长度为 16,刚好对应了 integer 的进制。因此可以将以上代码推广为N进制转换为十进制,其中N进制取决于 charset 的长度。

python
def n2dec(integer, charset):
    digits = [charset.index(char) for char in integer][::-1]
    powers = range(len(integer))
    pairs = zip(digits, powers)
    base = len(charset)
    products = map(lambda pair: pair[0] * base ** pair[1], pairs)
    summary = sum(products)
    return summary

精简代码后可以写成:

python
def n2dec(x, charset):
    base = len(charset)
    return sum(map(
        lambda p: p[0] * base ** p[1],
        zip([charset.index(c) for c in x][::-1], range(len(x))),
    ))

负数进制转换

大于二进制的进制的负数一般用原码表示,即符号位+绝对值。因此在只考虑负号的情况下,只需:

python
def n2dec(x, charset):
    base = len(charset)
    negative = x.startswith('-')  # 检测是否为负号开头
    x = x[1:] if negative else x  # 去掉开头的负号
    i = sum(map(
        lambda p: p[0] * base ** p[1],
        zip([charset.index(c) for c in x][::-1], range(len(x))),
    ))
    return -i if negative else i