Page 1 of 1

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

Posted: Thu May 25, 2023 7:42 pm
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

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

Posted: Fri May 26, 2023 3:41 am
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

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

Posted: Fri May 26, 2023 9:23 am
by blumenwesen

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