How do I get bc(1) to print the leading zero?
BashUnixBcBash Problem Overview
I do something like the following in a Makefile:
echo "0.1 + 0.1" | bc
(in the real file the numbers are dynamic, of course)
It prints .2
but I want it to print 0.2
.
I would like to do this without resorting to sed
but I can't seem to find how to get bc
to print the zero. Or is bc
just not able to do this?
Bash Solutions
Solution 1 - Bash
You can also resort to awk to format:
echo "0.1 + 0.1" | bc | awk '{printf "%f", $0}'
or with awk itself doing the math:
echo "0.1 0.1" | awk '{printf "%f", $1 + $2}'
Solution 2 - Bash
This might work for you:
echo "x=0.1 + 0.1; if(x<1) print 0; x" | bc
Solution 3 - Bash
After a quick look at the source (see bc_out_num()
, line 1461), I don't see an obvious way to make the leading 0
get printed if the integer portion is 0
. Unless I missed something, this behaviour is not dependent on a parameter which can be changed using command-line flag.
Short answer: no, I don't think there's a way to make bc
print numbers the way you want.
I don't see anything wrong with using sed
if you still want to use bc
. The following doesn't look that ghastly, IMHO:
[me@home]$ echo "0.1 + 0.1" | bc | sed 's/^\./0./'
0.2
If you really want to avoid sed
, both eljunior's and choroba's suggestions are pretty neat, but they require value-dependent tweaking to avoid trailing zeros. That may or may not be an issue for you.
Solution 4 - Bash
I cannot find anything about output format in the documentation. Instead of sed, you can also reach for printf:
printf '%3.1f\n' $(bc<<<0.1+0.1)
Solution 5 - Bash
echo "$a / $b" | bc -l | sed -e 's/^-\./-0./' -e 's/^\./0./'
This should work for all cases where the results are:
- "-.123"
- ".123"
- "-1.23"
- "1.23"
Explanation:
-
For everything that only starts with
-.
, replace-.
with-0.
-
For everything that only starts with
.
, replace.
with0.
Solution 6 - Bash
This one will also handle negative numbers:
echo "0.1 - 0.3" | bc | sed -r 's/^(-?)\./\10./'
Solution 7 - Bash
$ bc -l <<< 'x=-1/2; if (length (x) == scale (x) && x != 0) { if (x < 0) print "-",0,-x else print 0,x } else print x'
This one is pure bc
. It detects the leading zero by comparing the result of the length
with the scale
of the expression. It works on both positive and negative number.
Solution 8 - Bash
Building on potongs answer,
For fractional results:
echo "x=0.1 + 0.1; if(x<1 && x > 0) print 0; x" | bc -l
Note that negative results will not be displayed correctly. Aquarius Power has a solution for that.
Solution 9 - Bash
For positive numbers, it may be as simple as printing (an string) zero:
$ echo '"0";0.1+0.1' | bc
0.2
avoid the zero if the number is bigger (or equal) to 1:
$ echo 'x=0.1+0.1; if(x<1){"0"}; x' | bc
0.2
It gets a bit more complex if the number may be negative:
echo 'x= 0.3 - 0.5 ; s=1;if(x<0){s=-1};x*=s;if(s<0){"-"};if(x<1) {"0"};x' | bc
-0.2
You may define a function and add it to a library:
$ echo 'define leadzero(x){auto s;
s=1;if(x<0){s=-1};x*=s;if(s<0){"-"};if(x<1){"0"};
return(x)};
leadzero(2.1-12.4)' | bc
-10.3
$ echo 'define leadzero(x){auto s;
s=1;if(x<0){s=-1};x*=s;if(s<0){"-"};if(x<1){"0"};
return(x)};
leadzero(0.1-0.4)' | bc
-0.3
Solution 10 - Bash
Probably, bc
isn't really the best "bench calculator" for the modern age. Other languages will give you more control. Here are working examples that print values in the range (-1.0..+1.0) with a leading zero. These examples use bc
, AWK
, and Python 3
, along with Here String syntax.
#!/bin/bash
echo "using bc"
time for (( i=-2; i<=+2; i++ ))
{
echo $(bc<<<"scale=1; x=$i/2; if (x==0||x<=-1||x>=1) { print x } else { if (x<0) { print \"-0\";-x } else { print \"0\";x } } ")
}
echo
echo "using awk"
time for (( i=-2; i<=+2; i++ ))
{
echo $(echo|awk "{printf \"%.1f\",$i/2}")
}
echo
echo "using Python"
time for (( i=-2; i<=+2; i++ ))
{
echo $(python3<<<"print($i/2)")
}
Note that the Python version is about 10x slower, if that matters (still very fast for most purposes).
Doing any non-trivial math with sh
or bc
is a fool's errand. There are much better bench calculators available nowadays. For example, you can embed and execute Python subroutines inside your Bash scripts using Here Documents.
function mathformatdemo {
python3<<SCRIPT
import sys
from math import *
x=${1} ## capture the parameter from the shell
if -1<=x<=+1:
#print("debug: "+str(x),file=sys.stderr)
y=2*asin(x)
print("2*asin({:2.0f})={:+6.2f}".format(x,y))
else: print("domain err")
SCRIPT
}
echo "using Python via Here-doc"
time for (( i=-2; i<=+2; i++ ))
{
echo $(mathformatdemo $i)
}
Output:
using Python via Here-doc
domain err
2*asin(-1)= -3.14
2*asin( 0)= +0.00
2*asin( 1)= +3.14
domain err
Solution 11 - Bash
this only uses bc, and works with negative numbers:
bc <<< "x=-.1; if(x==0) print \"0.0\" else if(x>0 && x<1) print 0,x else if(x>-1 && x<0) print \"-0\",-x else print x";
try it with:
for y in "0" "0.1" "-0.1" "1.1" "-1.1"; do
bc <<< "x=$y; if(x==0) print \"0.0\" else if(x>0 && x<1) print 0,x else if(x>-1 && x<0) print \"-0\",-x else print x";
echo;
done
Solution 12 - Bash
Another simple way, similar to one of the posts in this thread here:
echo 'x=0.1+0.1; print "0",x,"\n"' | bc
Print the list of variables, including the leading 0 and the newline.
Solution 13 - Bash
Since you have the question tagged [bash]
you can simply compute the answer and save it to a variable using command substitution (e.g. r="$(...)"
) and then using [[..]]
with =~
to test if the first character in the result is [1-9]
(e.g. [[ $r =~ ^[1-9].*$ ]]
), and if the first character isn't, prepend '0'
to the beginning of r
, e.g.
r=$(echo "0.1 + 0.1" | bc) # compute / save result
[[ $r =~ ^[1-9].*$ ]] || r="0$r" # test 1st char [1-9] or prepend 0
echo "$r" # output result
Result
0.2
If the result r
is 1.0
or greater, then no zero is prepended, e.g. (as a 1-liner)
$ r=$(echo "0.8 + 0.6" | bc); [[ $r =~ ^[1-9].*$ ]] || r="0$r"; echo "$r"
1.4