Bash: digital numbers in bash without awk, bc, expr, printf

For discussions about programming, and for programming questions and advice


Moderator: Forum moderators

Post Reply
blumenwesen
Posts: 32
Joined: Sun Apr 10, 2022 10:02 pm

Bash: digital numbers in bash without awk, bc, expr, printf

Post by blumenwesen »

Hi have tried to calculate digital numbers, but without awk, bc, expr, printf.
The first variant does not come over "9223372036854775808" as with "echo -e $[2**63+1]" returns 0 again from the limit.
The second variant multiplies only the crossing points of two single digits, then puts them together and adds the double or triple numbers to the front number.
The variant comes over the limit of "9223372036854775808" but apparently has problems with the single digit multiplier.

Code: Select all

a="2"
for ((z=0; z<65; z++)); do 
b="2"; y=""; 
for ((x=${#a}-1; x>=0; x--)); do y+="${a:$x:1} "; done; ar0=($y); w=""; 
for ((v=${#b}-1; v>=0; v--)); do w+="${b:$v:1} "; done; ar1=($w); c=""; 
for ((y=0; y<${#a}; y++)); do for ((x=0; x<${#b}; x++)); do 
d=${ar0[$y]}$(for ((w=0; w<$y; w++)); do echo -n "0"; done); 
e=${ar1[$x]}$(for ((v=0; v<$x; v++)); do echo -n "0"; done); 
c=$(echo -e $[$d*$e])"+"$c; 
done; done; 
a=$(echo -e $["${c:0:${#c}-1}"])
echo $a
done

Code: Select all

a="16" # digital number / single digits do not work
for ((z=0; z<100; z++)); do 
b="16"; # should be possible with 2
c=""; for ((y=${#a}-1; y>=0; y--)); do c+=${a:$y:1}; done; a=$c; c=""; for ((x=${#b}-1; x>=0; x--)); do c+=${b:$x:1}; done; b=$c; c=""; # reverse numbers
for ((y=0; y<${#a}; y++)); do for ((x=0; x<${#b}; x++)); do 
d=$[$y+$x]; c+="$d-$[${a:$y:1}*${b:$x:1}] "; # single digit multiplication
done; done; arr=($c); c=""; 
for ((y=0; y<$d+1; y++)); do for ((x=0; x<${#arr[@]}; x++)); do 
[[ $y == $x ]] && continue; 
[[ $y == ${arr[$x]%[-]*} ]] && arr[$y]=$[${arr[$y]#*[-]}+${arr[$x]#*[-]}] && arr[$x]=""; # single result addition
done; c+="${arr[$y]} "; done; arr=(${c##*[-]}); c=""; 
for ((y=0; y<${#arr[@]}; y++)); do 
[[ ${#arr[$y]} == 1 ]] && c+=${arr[$y]}; # single digit position addition
[[ ${#arr[$y]} == 2 ]] && c+=${arr[$y]:1:1} && arr[$y+1]=$[${arr[$y+1]}+${arr[$y]:0:1}]; # double digit position addition
[[ ${#arr[$y]} == 3 ]] && c+=${arr[$y]:2:1} && arr[$y+1]=$[${arr[$y+1]}+${arr[$y]:1:1}] && arr[$y+2]=$[${arr[$y+2]}+${arr[$y]:0:1}]; # triple digit position addition
done; d=""; 
for ((x=${#c}-1; x>=0; x--)); do d+=${c:$x:1}; done; # reverse numbers
a=$d; echo $d # result
done
User avatar
MochiMoppel
Posts: 1123
Joined: Mon Jun 15, 2020 6:25 am
Location: Japan
Has thanked: 17 times
Been thanked: 361 times

Re: digital numbers in bash without awk, bc, expr, printf

Post by MochiMoppel »

blumenwesen wrote: Thu May 25, 2023 7:42 pm

Hi have tried to calculate digital numbers, but without awk, bc, expr, printf.
The first variant does not come over "9223372036854775808" as with "echo -e $[2**63+1]" returns 0 again from the limit.

This should do and MUCH faster. For exponentiation $a and $b must be set to same (base) value and $c becomes exponent.
No limitation to $a and $c. Value of $b must not exceed 17digits. This should give you plenty of room to calculate the most insane numbers.

Code: Select all

#!/bin/bash
a=2   #initial 1st factor 
b=2   #fixed 2nd factor
c=64  #max iteration
for ((i=2;i<=$c;i++)); do
res=
cov=
while [ $a ] ;do
	m=$((${a#${a%?}}*$b+cov))   #multiply last digit of $a with $b and add any remainder to be carried over from previous multiplication
	res=$((m%10))$res           #last digit of m prepended to accumulated result
	cov=$((m/10))               #carry-over for next loop: all digits of m except last. Returns '0' for single digit result.
	a=${a%?}                    #cut last digit
done
a=${cov#0}$res                  #prepend any remaining carry-over value if not 0
echo "Iterations $i:$a"
done

This would even be faster. Without the bash specific for..done loop it becomes POSIX compliant and can run with busybox ash. In my case twice as fast.

Code: Select all

#!/bin/ash
a=2   #initial 1st factor 
b=2   #fixed 2nd factor
c=64  #max iteration
i=1   #iteration
while [ $i -lt $c ]; do
	res=
	cov=
	i=$((i+1))
	while [ $a ] ;do
		m=$((${a#${a%?}}*$b+cov))   #multiply last digit of $a with $b and add any remainder to be carried over from previous multiplication
		res=$((m%10))$res           #last digit of m prepended to accumulated result
		cov=$((m/10))               #carry-over for next loop: all digits of m except last. Returns '0' for single digit result.
		a=${a%?}                    #cut last digit
	done
	a=${cov#0}$res                  #prepend any remaining carry-over value if not 0
	echo "Iterations $i:$a"
done
blumenwesen
Posts: 32
Joined: Sun Apr 10, 2022 10:02 pm

Re: digital numbers in bash without awk, bc, expr, printf

Post by blumenwesen »

Wow thank you, I made it really awkward before. :thumbup:

Post Reply

Return to “Programming”