[英]Hex to Dec conversion with printf in sh fail for more than 16 digits
我只有 shell 可用,沒有 bash、Perl、python 等。
使用printf
小數字工作:
root@DD-WRT:/jffs# printf "%d\n", 0x15a
346
但是大量失敗。
root@DD-WRT:/jffs# printf "%d\n", 0x15abc12345afda325
sh: invalid number '0x15abc12345afda325'
0
也可以使用 shell 對例如模塊執行十六進制運算嗎?
這是什么殼? 在Linux上,我看到:
$ bash -c 'echo $((0x15abc12345afda325))'
6538120775109288741
錯誤
$ dash -c 'echo $((0x15abc12345afda325))'
9223372036854775807
錯誤
$ ksh -c 'echo $((0x15abc12345afda325))'
2.49848648488188404e+19
正確,但輸出格式錯誤
$ ksh -c 'printf "%d\n" $((0x15abc12345afda325))'
ksh: printf: warning: 2.49848648488188404e+19: overflow exception
9223372036854775807
$ ksh -c 'printf "%.0f\n" $((0x15abc12345afda325))'
24984864848818840399
GNU AWK
$ gawk -v n=0x15abc12345afda325 'BEGIN {print strtonum(n)}'
24984864848818839552
$ gawk --bignum -v n=0x15abc12345afda325 'BEGIN {print strtonum(n)}'
24984864848818840357
您有可用的bc
嗎?
$ hex=15abc12345afda325
$ echo "ibase=16; $hex" | bc
(standard_in) 1: syntax error
十六進制值需要大寫嗎?
$ echo "ibase=16; ${hex^^}" | bc
24984864848818840357
嗯,不同於ksh輸出。 WolframAlpha說24984864848818840357
我看到busybox帶有dc
,但可悲的是,它已癱瘓:
$ printf "%s\n" 16 i 15ABC12345AFDA325 p | dc
24984864848818840357
$ printf "%s\n" 16 i 15ABC12345AFDA325 p | busybox dc
dc: syntax error at 'i'
我試圖用純sh(實際上是ash,因為busybox sh
運行內置的ash)編寫一個從十六進制到十進制的任意精度轉換器 。 由於功能有限(沒有數組)和“奇怪的”錯誤(沒有明確的文檔說明)(例如表達式中不允許使用空格),因此與bash相比,它需要付出更多的努力。
#!/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位肢體來避免乘法時溢出。 我選擇輸出基數為1 000 000 000
因為它是32位int表示的10的最大冪。 然后輸入基數必須小於基數(需要較少的進位處理),因此我選擇0x10000000,即16的最大冪小於1000000000
當然,如果您的busybox嚴重受損,不支持64位int,則必須使用基數0x1000並一次處理3個十六進制數字
用bc確認,每次結果相同
$ 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
你需要多少位數?
printf '0x15abc12345afda965317742584595757
515927957157574571592325FFFFFFFFFFFF' |
gawk -nMbe '$++NF = +$_'
0x15abc12345afda96531774258459575751
5927957157574571592325FFFFFFFFFFFF
642386077699778282185783828
049060173534486064360167379
481875931384718910679941119
從技術上講, gawk -nM '$++NF = +$_'
就足夠了;
-- -b
: 對於字節模式:加快速度
-- -e
: 只是為了明確說明主要代碼是通過命令行獲取的
如果您只是想要沒有原始輸入的答案,則更簡單:
gawk -nM '$_+=_'
642386077699778282185783828
049060173534486064360167379
481875931384718910679941119
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.