76

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?

rwos
  • 1,661
  • 1
  • 15
  • 18
  • 6
    After reading all these answers I think it is suprising you accepted as solution an answer which uses the `printf` command. Then, I guess you actually did not want ***to get bc(1) to print the leading zero***. – Jdamian Aug 25 '17 at 07:01
  • 4
    As of this comment all solutions manipulate the output of [tag:bc] in some form or another. So apparently the short answer is _no you can't_ because [tag:bc] itself lacks the proper formatting options. – dtmland Jul 13 '20 at 02:05

13 Answers13

47

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}'
Elias Dorneles
  • 20,592
  • 10
  • 76
  • 100
40

This might work for you:

echo "x=0.1 + 0.1; if(x<1) print 0; x" | bc
potong
  • 51,370
  • 6
  • 49
  • 80
  • 3
    Probably worth adding another conditional to check you're not clobbering a negative number, as if you're not careful the following `echo "scale=3; x=-0.5; if(x<1) print 0; x" | bc` would yield 0-.5 . the following `echo "scale=3; x=-0.5; if(x>0 && x<1) print 0; x" | bc` prints `-.5`, which isn't quite right. The following is more robust, but verbose and cumbersome. YMMV. `echo 'scale=3; x=-0.5; if(x>-1 && x<1) { if(x<0) { print "-"; x=0-x }; print 0} ; x' | bc` – Matthew Strasiotto Oct 18 '20 at 16:39
32

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.

Community
  • 1
  • 1
Shawn Chin
  • 79,172
  • 18
  • 156
  • 188
  • 1
    Also doesn't work with negative numbers, `echo "0.1 - 0.2" | bc | sed 's/^\./0./'`. If you tag on a `| sed 's/^-\./-0./'` it will, but then it doesn't look all that slick any more, `echo "0.1 - 0.2" | bc | sed 's/^\./0./' | sed 's/^-\./-0./'` – AkselA Apr 19 '17 at 11:02
13

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)
choroba
  • 216,930
  • 22
  • 195
  • 267
  • well, no, actually: `$ printf %f $(bc<<<0.1+0.1) -bash: printf: .2: invalid number`. **Edit:** my bad, locale issue. This works. – rwos Dec 06 '11 at 15:26
  • Huh. What version of bash do you run? – choroba Dec 06 '11 at 15:27
  • `$ printf '%3.1f\n' .02` -> `0.0`. Oops. – unbeli Dec 06 '11 at 15:30
  • 1
    The .02 thing above doesn't matter. The Locale thing does. It makes the whole thing pretty ugly: `LANG=C;printf %1.1f $(bc<<<0.1+0.1)` that's not much nicer than using sed... :) – rwos Dec 06 '11 at 15:33
  • 3
    @rwos I second that. sed's a lot cleaner `echo ".1+.1" | bc | sed 's/^\./0./'` – Shawn Chin Dec 06 '11 at 15:40
  • 1
    @chobra (+1) .... @unbeli. It works fine.. The reason you got `0.0` is because that is what you asked for! `3.1f` means a minimum of 3 output chars inclding the decimal point and sign.. The `.1` means print **only** one digit after the decimal point. ... @rwos. Why do you think you need LANG=C to handle only ASCII digits? ... It certainly isn't an issue in my Linux Ubuntu using the standard UTF-8 locale. – Peter.O Feb 26 '12 at 12:13
  • 1
    `printf "%g\n" $(bc<<<0.1+0.1)` :-) – anishsane Apr 28 '14 at 15:02
5

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.

cafemike
  • 660
  • 5
  • 15
  • With: sed -e 's/\(^\|\s\)-\./-0./' -e 's/\(^\.\|\s\.\)/0./' it would match even inside the line, if there is an empty space sign before the number. Like the output off echo ' "fraction: ";1/3 ' | bc -l – Martin T. Dec 07 '18 at 08:40
3

This one will also handle negative numbers:

echo "0.1 - 0.3" | bc | sed -r 's/^(-?)\./\10./'
Benjamin W.
  • 38,596
  • 16
  • 96
  • 104
3
$ 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.

gomibako
  • 73
  • 1
  • 5
3

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.

Community
  • 1
  • 1
3

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
done
  • 6,370
  • 27
  • 49
2

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
Brent Bradburn
  • 46,639
  • 15
  • 140
  • 156
  • Similar answer to a related question: [How do I use floating-point division in bash?](https://stackoverflow.com/a/28541396/86967) – Brent Bradburn Oct 11 '19 at 21:39
1

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
Aquarius Power
  • 3,441
  • 5
  • 30
  • 63
0

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.

ssanch
  • 361
  • 2
  • 6
0

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
David C. Rankin
  • 75,900
  • 6
  • 54
  • 79