使用sh的printf进行的十六进制到Dec转换失败超过16位数字 [英] Hex to Dec conversion with printf in sh fail for more than 16 digits

查看:281
本文介绍了使用sh的printf进行的十六进制到Dec转换失败超过16位数字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只有外壳可用,没有bash,Perl,python等。

I only have shell available no bash, Perl, python etc.

使用 printf 小数字可以工作:

Using printf small numbers work:

root@DD-WRT:/jffs# printf "%d\n", 0x15a
346

但是大量失败。

root@DD-WRT:/jffs# printf "%d\n", 0x15abc12345afda325
sh: invalid number '0x15abc12345afda325'
0

也可以使用shell执行十六进制算术,例如Module吗?

Also is it possible to perform hexadecimal arithmetic for example Module using shell ?

推荐答案

我试图用纯sh(实际上是灰烬,因为 busybox sh 任意精度转换器 c $ c>运行内置的ash)。由于功能有限(无数组)和奇怪的错误(没有清晰的文档,例如表达式中不允许使用空格),因此它比bash花费了更多的精力。

I've attempted to write an arbitrary-precision converter from hexadecimal to decimal in pure sh (actually ash, since busybox sh runs the built-in ash). It needs a lot more effort than bash due to the limited set of features (no arrays) and "strange" errors without clear documentation (like spaces not allowed in expressions)

#!/bin/ash

obase=1000000000    # 1e9, the largest power of 10 that fits in int32_t
ibase=$((1 << 7*4)) # only 7 hex digits, because 0xFFFFFFFF > 1e9

inp="000000${1#0x}"                 # input value in $1 with optional 0x
inp=${inp:$((${#inp}%7)):${#inp}}   # pad the string length to a multiple of 7

carry=0
# workaround, since sh and ash don't support arrays
result0=0       # output digits will be stored in resultX variables in little endian
MSDindex=0      # index of the most significant digit in the result

print_result()
{
    eval echo -n \$result$MSDindex  # print MSD
    if [ $MSDindex -gt 0 ]; then    # print remaining digits
        for i in $(seq $((MSDindex-1)) -1 0); do eval printf "%09d" \$result$i; done
    fi
    echo
}

# Multiply a digit with the result
# $1 contains the value to multiply with the result array
mul()
{
    carry=0
    for i in $(seq 0 $MSDindex); do
        eval let res="$1\\*result$i+carry"
        eval let result$i=res%obase
        let carry=res/obase
    done

    while [ $carry -ne 0 ]; do
        let MSDindex=MSDindex+1
        eval let result$MSDindex=carry%obase
        let carry=carry/obase
    done
}

# Add a digit with the result
# $1 contains the digit to add with the array
add()
{
    eval let res=$1+result0
    eval let result0=res%obase
    let carry=res/obase

    i=1
    while [ $carry -ne 0 ]
    do
        eval let res=carry+result$i
        eval let result$i=res%obase
        let carry=res/obase
        if [ $i -gt $MSDindex ]; then MSDindex=$i; fi
        let i=i+1
    done
}

# main conversion loop
while [ -n "$inp" ]     # iterate through the hex digits, 7 at a time
do
    hexdigit=${inp:0:7}
    mul $ibase          # result = result*input_base+hexdigit
    add 0x$hexdigit

    if [ ${#inp} -gt 7 ]; then
        inp=${inp: $((7-${#inp}))}
    else
        unset inp
    fi
done

print_result

我在Ubuntu中使用busybox进行检查,发现它支持64位算术,因此,我需要一个32位的肢体来避免乘法时的溢出。我将输出基数选择为 1000000000000 ,因为它是32位int表示的10的最大幂。然后输入基数必须小于基数(需要较少的进位处理),因此我选择0x10000000,即16的最大幂小于1000000000

I checked with busybox in my Ubuntu and saw that it supports 64-bit arithmetic, therefore I need a 32-bit limb to avoid overflow when multiply. I choose the output base as 1 000 000 000 because it's the largest power of 10 that can be represented in a 32-bit int. Then the input base need to be smaller than the base (less carry handling needed), thus I choose 0x10000000, the largest power of 16 that's smaller than 1000000000

您的busybox太残缺了,它不支持64位int,那么您必须使用基数0x1000并一次处理3个十六进制数字

Of course if your busybox is so crippled that it doesn't support 64-bit int then you have to use base 0x1000 and process 3 hexadecimal digits at once

与bc确认,结果每次都是相同的

Confirmed with bc, the result is the same every time

$ v=15ABC12345AFDA325; busybox sh ./hex2dec.sh $v; echo "ibase=16; $v" | bc
24984864848818840357
24984864848818840357
$ v=2B37340113436BA5C23513A1231111C; busybox sh ./hex2dec.sh $v; echo "ibase=16; $v" | bc
3590214682278754501437472025955340572
3590214682278754501437472025955340572
$ v=60431BCD73610ADF2B37340113436BA5C23513A12311111111111;\
> busybox sh ./hex2dec.sh $v; echo "ibase=16; $v" | bc
2474996796503602902399592755755761709869730986038055786310078737
2474996796503602902399592755755761709869730986038055786310078737

这篇关于使用sh的printf进行的十六进制到Dec转换失败超过16位数字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆