#!/bin/bash shopt -s extglob # Bash arithmetic is in 64-bit integers, from -2^63 to 2^63-1. # We emulate floating point arithmetic with paired integers for integer and # fractional part; we work to 9 figures as 2^63~=9.22e18. # A better implementation would use true floating-point arithmetic. function flop() { local op=$2 local x=$1 local y=$3 local sig mod num ipart fpart places raise local a for a in x y; do if [[ ${!a} == -* ]]; then sig=- mod=${!a:1} elif [[ ${!a} == +* ]]; then sig=+ mod=${!a:1} else sig=+ mod=${!a} fi mod=${mod/#+(0)}; [[ "$mod" ]] || mod=0 num=${mod/.} case ${mod} in +([[:digit:]])?(.)) ipart=${mod/.}; fpart=0; places=0;; .+([[:digit:]])) ipart=0; fpart=${mod/.}; places=${#fpart};; +([[:digit:]]).+([[:digit:]])) ipart=${mod/%.*}; fpart=${mod/#*.}; places=${#fpart};; *) num=0; ipart=0; fpart=0; places=0;; esac fpart=${fpart/#+(0)}; [[ "$fpart" ]] || fpart=0 ipart=$sig$ipart; fpart=$sig$fpart # Pure evil {^_^} local name for name in sig mod num ipart fpart places; do eval $a$name=${!name} done done case $op in [+-]) ipart=$((xipart $op yipart)) places=$((xplaces > yplaces ? xplaces : yplaces)) if [[ $places -ge 17 ]]; then # Include sign in length xfpart=${xfpart:0:17} yfpart=${yfpart:0:17} xplaces=$((xplaces > 16 ? 16 : xplaces)) yplaces=$((yplaces > 16 ? 16 : yplaces)) places=16 fi fpart=$((xfpart * 10 ** (places - xplaces) $op yfpart * 10 ** (places - yplaces))) if [[ $fpart -lt 0 ]]; then ipart=$((ipart - 1)) fpart=$((10 ** places + fpart)) elif [[ $fpart -gt $((10 ** places)) ]]; then ipart=$((ipart + 1)) fpart=$((fpart - 10 ** places)) fi if [[ ${#fpart} -lt $places ]]; then fpart=$((10 ** (places - ${#fpart})))$fpart fpart=${fpart:1} fi ipart=${ipart/#+} fpart=${fpart/%+(0)} echo $ipart.$fpart ;; [×]) places=$((xplaces + yplaces)) if [[ $places -ge 18 ]]; then xnum=${xnum:0:9} ynum=${ynum:0:9} places=$(((xplaces > 8 ? 8 : xplaces) + (yplaces > 8 ? 8 : yplaces))) fi num=$((xnum * ynum)) if [[ ${#num} -ge $places ]]; then ipart=${num:0:$((${#num} - places))} fpart=${num:$((${#num} - places))} elif [[ ${#num} -eq $places ]]; then ipart=0 fpart=$num else ipart=0 fpart=$((10 ** (places - ${#num})))$num fpart=${fpart:1} fi if [[ $xsig == $ysig ]]; then sig=; else sig=-; fi fpart=${fpart/%+(0)} echo $sig$ipart.$fpart ;; [÷]) if [[ $ynum -eq 0 ]]; then echo "Division by zero" >&2 return 1 fi raise=$((19 - ${#xnum})) num=$(((xnum * 10 ** raise) / ynum)) places=$((raise + xplaces - yplaces)) if [[ ${#num} -ge $places ]]; then ipart=${num:0:$((${#num} - places))} fpart=${num:$((${#num} - places))} elif [[ ${#num} -eq $places ]]; then ipart=0 fpart=$num else ipart=0 fpart=$((10 ** (places - ${#num})))$num fpart=${fpart:1} fi if [[ $xsig == $ysig ]]; then sig=; else sig=-; fi fpart=${fpart/%+(0)} echo $sig$ipart.$fpart ;; esac } declare -a stack while read line; do [[ "$line" ]] || exit 0 stack=( ) line=${line//\\*/×} for token in ${line//\//÷}; do case $token in [÷×+-]) if [[ ${#stack[@]} -gt 1 ]]; then result=$(flop ${stack[1]} $token ${stack[0]}) stack=( $result ${stack[@]:2} ) else echo "Stack underflow" fi ;; ?(-)+([[:digit:].])) stack=( $token ${stack[@]} ) ;; *) echo "Bad operator: $token" esac done [[ ${#stack[@]} -gt 0 ]] \ && echo ${stack[$((${#stack[@]}-1))]} done