使用Bash将十六进制转换为十进制

3

我看到了一些奇怪的东西。 我尝试使用 Bash Shell 将十六进制转换为十进制。

我使用了非常简单的命令。

$ g_receiverBeforeToken=1158e460913d00000
$ echo $((16#$g_receiverBeforeToken))
1553255926290448384

正如大家所知,这个结果应该是“20000000000000000000”。

当我输入其他任何十六进制数时,都是正确的。但只有1553255926290448384是奇怪的。


所以你的意思是,比如说 1553255926290448383 没问题吗?顺便问一下,你知道 20000000000000000000 大于 2**64 吗?你的机器字长可能是 64 位。64 位是 16 个十六进制数字。1158e460913d00000 是 17 个十六进制数字。 - lurker
2个回答

4

这里不仅仅是限制在那个数字上,而是任何大于7fffffffffffffff的数字,因为它使用了64位整数,这是最大的一个。超过这个的16位数字会被包装并变成负数(因为有符号整数采用二进制补码表示法):

$ echo $((16#7fffffffffffffff))
9223372036854775807
$ echo $((16#7fffffffffffffff + 1))
-9223372036854775808
$ echo $((16#8000000000000000))
-9223372036854775808

过去的ffffffffffffffff(也称为-1),它会回到零:

$ echo $((16#ffffffffffffffff))
-1
$ echo $((16#ffffffffffffffff + 1))
0
$ echo $((16#10000000000000000))
0

最终结果:只有最后的16个十六进制数字才是有效的;超过这个范围的内容将被截断并丢弃在64位整数表示的高位。

$ echo $((16#0000000000000010))
16
$ echo $((16#10000000000000010))
16
$ echo $((16#ffffffff0000000000000010))
16

由于1553255926290448384有17位数字,因此第一位数字以这种方式被省略:

$ echo $((16#1158e460913d00000))
1553255926290448384
$ echo $((16#158e460913d00000))
1553255926290448384
$ echo $((16#121345158e460913d00000))
1553255926290448384

3
您可以使用一个简短的脚本,使用bc作为基数转换来实现您想要的功能:
#!/bin/bash

## validate sufficient input
test -n "$1" || {
    printf "\n error: insufficient input. usage:  %s num [obase (2)] [ibase (10)]\n\n" "${0//*\//}"
    exit 1
}

## test for help
test "$1" = "-h" || test "$1" = "--help" && {
    printf "\n  usage:  %s num [obase (2)] [ibase (10)] -- to convert number\n\n" "${0//*\//}"
    exit 0
}

## validate numeric value given for conversion (bash only test)
ival="${1^^}"
[[ $ival =~ [^0-9A-F] ]] && {
    printf "\n error: invalid input. Input must be within upper/lower case hex character set [0-9A-Fa-f]\n\n"
    exit 1
}

ob=${2:-2}
ib=${3:-10}

# set obase first before ibase -- or weird things happen.
printf "obase=%d; ibase=%d; %s\n" $ob $ib $ival | bc

示例用法/输出

$ bash hex2dec.sh -h

  usage:  hex2dec.sh num [obase (2)] [ibase (10)] -- to convert number

使用你的示例:

$ bash hex2dec.sh 1158e460913d00000 10 16
20000000000000000000

如果您还需要以二进制格式呈现,这也很方便:

$ bash hex2dec.sh 1158e460913d00000 2 16
10001010110001110010001100000100100010011110100000000000000000000

当我在Mac上尝试使用ival="${1^^}"时,出现了错误的替换错误。 - qodeninja
1
这是bash的一部分,它将第一个位置参数中的所有值转换为大写。在您的Mac上,bash --version显示什么?在man bash中,${parameter^^pattern}位于“参数扩展”的更广泛部分下。它已经成为bash的一部分了一段时间。您确定没有使用#!/bin/sh或以sh scriptname运行它吗?另外,echo $SHELL显示什么? - David C. Rankin
谢谢,是的,这个Mac OS是bash 3.2;我认为大写转换只在bash 4+中可用...在这种情况下,我使用了tr,现在似乎可以工作了。还有一个问题,我对bc不太熟悉,但如果我想将十六进制字符串转换为基于62个字母数字的字符串,我该如何使用您的脚本?具体来说,我正在尝试将md5哈希缩短为较短的基于62个字符的字符串(并稍后将其恢复为md5)。 - qodeninja
1
如果您的输入是十进制并且您想要输出六十二进制,您可以再次使用 bc。例如,printf "obase=%d; ibase=%d; %s\n" $ob $ib $val | bc,其中 ob=62ib=10,而 val 是您想要从十进制转换为六十二进制的任何值。(根据需要调整您的 ib - David C. Rankin
谢谢您的澄清,这确实是因为我不熟悉bc而导致的问题;首先,我想直接将字符传递给bc;但我仍然没有弄清楚,只好将位置值相加而不是直接进行转换。 - qodeninja

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接