I'm still looking for a pure bc
answer to how to round just one value within a function, but here's a pure bash
answer:
#!/bin/bash
echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"
embiggen() {
local int precision fraction=""
if [ "$1" != "${1#*.}" ]; then # there is a decimal point
fraction="${1#*.}" # just the digits after the dot
fi
int="${1%.*}" # the float as a truncated integer
precision="${#fraction}" # the number of fractional digits
echo $(( 10**10 * $int$fraction / 10**$precision ))
}
# round down if negative
if [ "$float" != "${float#-}" ]
then round="-5000000000"
else round="5000000000"
fi
# calculate rounded answer (sans decimal point)
answer=$(( ( `embiggen $float` * 100 + $round ) / `embiggen 1.18` ))
int=${answer%??} # the answer as a truncated integer
echo $int.${answer#$int} # reassemble with correct precision
read -p "Press any key to continue..."
Basically, this carefully extracts the decimals, multiplies everything by 100 billion (10¹⁰, 10**10
in bash
), adjusts for precision and rounding, performs the actual division, divides back to the appropriate magnitude, and then reinserts the decimal.
Step-by-step:
The embiggen()
function assigns the truncated integer form of its argument to $int
and saves the numbers after the dot in $fraction
. The number of fractional digits is noted in $precision
. The math multiplies 10¹⁰ by the concatenation of $int
and $fraction
and then adjusts that to match the precision (e.g. embiggen 48.86
becomes 10¹⁰ × 4886 / 100 and returns 488600000000
which is 488,600,000,000).
We want a final precision of hundredths, so we multiply the first number by 100, add 5 for rounding purposes, and then divide the second number. This assignment of $answer
leaves us at a hundred times the final answer.
Now we need to add the decimal point. We assign a new $int
value to $answer
excluding its final two digits, then echo
it with a dot and the $answer
excluding the $int
value that's already taken care of. (Never mind the syntax highlighting bug that makes this appear like a comment)
(Bashism: exponentiation is not POSIX, so this is a bashism. A pure POSIX solution would require loops to add zeros rather than using powers of ten. Also, "embiggen" is a perfectly cromulant word.)
One of the main reasons I use zsh
as my shell is that it supports floating point math. The solution to this question is quite straightforward in zsh
:
printf %.2f $((float/1.18))
(I'd love to see somebody add a comment to this answer with the trick to enabling floating point arithmetic in bash
, but I'm pretty sure that such a feature doesn't yet exist.)
bc
cannot achieve what is being requested (or at least the question I was asking when I found this post), which is how to round decimals usingbc
(that happens to be called bybash
).r()
function, so you can usebc -le "r($float/1.18, 2)"
.