How do I get bc(1) to print the leading zero?

BashUnixBc

Bash 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:

  1. For everything that only starts with -., replace -. with -0.

  2. For everything that only starts with ., replace . with 0.

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionrwosView Question on Stackoverflow
Solution 1 - BashElias DornelesView Answer on Stackoverflow
Solution 2 - BashpotongView Answer on Stackoverflow
Solution 3 - BashShawn ChinView Answer on Stackoverflow
Solution 4 - BashchorobaView Answer on Stackoverflow
Solution 5 - BashcafemikeView Answer on Stackoverflow
Solution 6 - Bashuser3420895View Answer on Stackoverflow
Solution 7 - BashgomibakoView Answer on Stackoverflow
Solution 8 - BashOliver GrantView Answer on Stackoverflow
Solution 9 - BashdoneView Answer on Stackoverflow
Solution 10 - BashBrent BradburnView Answer on Stackoverflow
Solution 11 - BashAquarius PowerView Answer on Stackoverflow
Solution 12 - BashssanchView Answer on Stackoverflow
Solution 13 - BashDavid C. RankinView Answer on Stackoverflow